/src/gdal/frmts/iso8211/ddfsubfielddefn.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: ISO 8211 Access |
4 | | * Purpose: Implements the DDFSubfieldDefn class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Frank Warmerdam |
9 | | * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "iso8211.h" |
16 | | |
17 | | #include <cstdio> |
18 | | #include <cstdlib> |
19 | | #include <cstring> |
20 | | |
21 | | #include <algorithm> |
22 | | |
23 | | #include "cpl_conv.h" |
24 | | #include "cpl_error.h" |
25 | | #include "cpl_string.h" |
26 | | |
27 | | /************************************************************************/ |
28 | | /* DDFSubfieldDefn() */ |
29 | | /************************************************************************/ |
30 | | |
31 | 1.54M | DDFSubfieldDefn::DDFSubfieldDefn() = default; |
32 | | |
33 | | /************************************************************************/ |
34 | | /* ~DDFSubfieldDefn() */ |
35 | | /************************************************************************/ |
36 | | |
37 | 1.54M | DDFSubfieldDefn::~DDFSubfieldDefn() = default; |
38 | | |
39 | | /************************************************************************/ |
40 | | /* SetName() */ |
41 | | /************************************************************************/ |
42 | | |
43 | | void DDFSubfieldDefn::SetName(const char *pszNewName) |
44 | | |
45 | 1.54M | { |
46 | 1.54M | osName = pszNewName; |
47 | 1.54M | while (!osName.empty() && osName.back() == ' ') |
48 | 4.89k | osName.pop_back(); |
49 | 1.54M | } |
50 | | |
51 | | /************************************************************************/ |
52 | | /* SetFormat() */ |
53 | | /* */ |
54 | | /* While interpreting the format string we don't support: */ |
55 | | /* */ |
56 | | /* o Passing an explicit terminator for variable length field. */ |
57 | | /* o 'X' for unused data ... this should really be filtered */ |
58 | | /* out by DDFFieldDefn::ApplyFormats(), but isn't. */ |
59 | | /* o 'B' bitstrings that aren't a multiple of eight. */ |
60 | | /************************************************************************/ |
61 | | |
62 | | int DDFSubfieldDefn::SetFormat(const char *pszFormat) |
63 | | |
64 | 1.47M | { |
65 | 1.47M | osFormatString = pszFormat; |
66 | | |
67 | | /* -------------------------------------------------------------------- */ |
68 | | /* These values will likely be used. */ |
69 | | /* -------------------------------------------------------------------- */ |
70 | 1.47M | if (osFormatString.size() >= 2 && osFormatString[1] == '(') |
71 | 766k | { |
72 | 766k | nFormatWidth = atoi(osFormatString.c_str() + 2); |
73 | 766k | if (nFormatWidth < 0) |
74 | 986 | { |
75 | 986 | CPLError(CE_Failure, CPLE_AppDefined, "Format width %s is invalid.", |
76 | 986 | osFormatString.c_str() + 2); |
77 | 986 | return FALSE; |
78 | 986 | } |
79 | 765k | bIsVariable = nFormatWidth == 0; |
80 | 765k | } |
81 | 711k | else |
82 | 711k | bIsVariable = TRUE; |
83 | | |
84 | | /* -------------------------------------------------------------------- */ |
85 | | /* Interpret the format string. */ |
86 | | /* -------------------------------------------------------------------- */ |
87 | 1.47M | switch (osFormatString[0]) |
88 | 1.47M | { |
89 | 442k | case 'A': |
90 | 446k | case 'C': // It isn't clear to me how this is different than 'A' |
91 | 446k | eType = DDFString; |
92 | 446k | break; |
93 | | |
94 | 93.3k | case 'R': |
95 | 93.3k | eType = DDFFloat; |
96 | 93.3k | break; |
97 | | |
98 | 409k | case 'I': |
99 | 426k | case 'S': |
100 | 426k | eType = DDFInt; |
101 | 426k | break; |
102 | | |
103 | 3.89k | case 'B': |
104 | 507k | case 'b': |
105 | | // Is the width expressed in bits? (is it a bitstring) |
106 | 507k | bIsVariable = FALSE; |
107 | 507k | if (osFormatString[1] == '\0') |
108 | 454 | return FALSE; |
109 | | |
110 | 506k | if (osFormatString[1] == '(') |
111 | 6.08k | { |
112 | 6.08k | nFormatWidth = atoi(osFormatString.c_str() + 2); |
113 | 6.08k | if (nFormatWidth < 0 || nFormatWidth % 8 != 0) |
114 | 541 | { |
115 | 541 | CPLError(CE_Failure, CPLE_AppDefined, |
116 | 541 | "Format width %s is invalid.", |
117 | 541 | osFormatString.c_str() + 2); |
118 | 541 | return FALSE; |
119 | 541 | } |
120 | | |
121 | 5.54k | nFormatWidth = nFormatWidth / 8; |
122 | 5.54k | eBinaryFormat = SInt; // good default, works for SDTS. |
123 | | |
124 | 5.54k | if (nFormatWidth < 5) |
125 | 4.73k | eType = DDFInt; |
126 | 816 | else |
127 | 816 | eType = DDFBinaryString; |
128 | 5.54k | } |
129 | | |
130 | | // or do we have a binary type indicator? (is it binary) |
131 | 500k | else |
132 | 500k | { |
133 | 500k | if (osFormatString[1] < '0' || osFormatString[1] > '5') |
134 | 1.66k | { |
135 | 1.66k | CPLError(CE_Failure, CPLE_AppDefined, |
136 | 1.66k | "Binary format = %c is invalid.", |
137 | 1.66k | osFormatString[1]); |
138 | 1.66k | return FALSE; |
139 | 1.66k | } |
140 | 499k | eBinaryFormat = |
141 | 499k | static_cast<DDFBinaryFormat>(osFormatString[1] - '0'); |
142 | 499k | nFormatWidth = atoi(osFormatString.c_str() + 2); |
143 | 499k | if (nFormatWidth < 0) |
144 | 737 | { |
145 | 737 | CPLError(CE_Failure, CPLE_AppDefined, |
146 | 737 | "Format width %s is invalid.", |
147 | 737 | osFormatString.c_str() + 2); |
148 | 737 | return FALSE; |
149 | 737 | } |
150 | | |
151 | 498k | if (eBinaryFormat == SInt || eBinaryFormat == UInt) |
152 | 467k | eType = DDFInt; |
153 | 30.8k | else |
154 | 30.8k | eType = DDFFloat; |
155 | 498k | } |
156 | 503k | break; |
157 | | |
158 | 503k | case 'X': |
159 | | // 'X' is extra space, and should not be directly assigned to a |
160 | | // subfield ... I have not encountered it in use yet though. |
161 | 915 | CPLError(CE_Failure, CPLE_AppDefined, |
162 | 915 | "Format type of `%c' not supported.\n", osFormatString[0]); |
163 | | |
164 | 915 | return FALSE; |
165 | | |
166 | 2.90k | default: |
167 | 2.90k | CPLError(CE_Failure, CPLE_AppDefined, |
168 | 2.90k | "Format type of `%c' not recognised.\n", |
169 | 2.90k | osFormatString[0]); |
170 | | |
171 | 2.90k | return FALSE; |
172 | 1.47M | } |
173 | | |
174 | 1.47M | return TRUE; |
175 | 1.47M | } |
176 | | |
177 | | /************************************************************************/ |
178 | | /* Dump() */ |
179 | | /************************************************************************/ |
180 | | |
181 | | /** |
182 | | * Write out subfield definition info to debugging file. |
183 | | * |
184 | | * A variety of information about this field definition is written to the |
185 | | * give debugging file handle. |
186 | | * |
187 | | * @param fp The standard IO file handle to write to. i.e. stderr |
188 | | */ |
189 | | |
190 | | void DDFSubfieldDefn::Dump(FILE *fp, int nNestingLevel) const |
191 | | |
192 | 0 | { |
193 | 0 | std::string osIndent; |
194 | 0 | for (int i = 0; i < nNestingLevel; ++i) |
195 | 0 | osIndent += " "; |
196 | |
|
197 | 0 | #define Print(...) \ |
198 | 0 | do \ |
199 | 0 | { \ |
200 | 0 | fprintf(fp, "%s", osIndent.c_str()); \ |
201 | 0 | fprintf(fp, __VA_ARGS__); \ |
202 | 0 | } while (0) |
203 | |
|
204 | 0 | Print("DDFSubfieldDefn:\n"); |
205 | 0 | Print(" Label = `%s'\n", osName.c_str()); |
206 | 0 | Print(" FormatString = `%s'\n", osFormatString.c_str()); |
207 | 0 | } |
208 | | |
209 | | /************************************************************************/ |
210 | | /* GetDataLength() */ |
211 | | /* */ |
212 | | /* This method will scan for the end of a variable field. */ |
213 | | /************************************************************************/ |
214 | | |
215 | | /** |
216 | | * Scan for the end of variable length data. Given a pointer to the data |
217 | | * for this subfield (from within a DDFRecord) this method will return the |
218 | | * number of bytes which are data for this subfield. The number of bytes |
219 | | * consumed as part of this field can also be fetched. This number may |
220 | | * be one longer than the length if there is a terminator character |
221 | | * used.<p> |
222 | | * |
223 | | * This method is mainly for internal use, or for applications which |
224 | | * want the raw binary data to interpret themselves. Otherwise use one |
225 | | * of ExtractStringData(), ExtractIntData() or ExtractFloatData(). |
226 | | * |
227 | | * @param pachSourceData The pointer to the raw data for this field. This |
228 | | * may have come from DDFRecord::GetData(), taking into account skip factors |
229 | | * over previous subfields data. |
230 | | * @param nMaxBytes The maximum number of bytes that are accessible after |
231 | | * pachSourceData. |
232 | | * @param pnConsumedBytes Pointer to an integer into which the number of |
233 | | * bytes consumed by this field should be written. May be NULL to ignore. |
234 | | * |
235 | | * @return The number of bytes at pachSourceData which are actual data for |
236 | | * this record (not including unit, or field terminator). |
237 | | */ |
238 | | |
239 | | int DDFSubfieldDefn::GetDataLength(const char *pachSourceData, int nMaxBytes, |
240 | | int *pnConsumedBytes) const |
241 | | |
242 | 7.93M | { |
243 | 7.93M | if (!bIsVariable) |
244 | 5.59M | { |
245 | 5.59M | if (nFormatWidth > nMaxBytes) |
246 | 3.74k | { |
247 | 3.74k | CPLError(CE_Warning, CPLE_AppDefined, |
248 | 3.74k | "Only %d bytes available for subfield %s with\n" |
249 | 3.74k | "format string %s ... returning shortened data.", |
250 | 3.74k | nMaxBytes, osName.c_str(), osFormatString.c_str()); |
251 | | |
252 | 3.74k | if (pnConsumedBytes != nullptr) |
253 | 2.41k | *pnConsumedBytes = nMaxBytes; |
254 | | |
255 | 3.74k | return nMaxBytes; |
256 | 3.74k | } |
257 | 5.59M | else |
258 | 5.59M | { |
259 | 5.59M | if (pnConsumedBytes != nullptr) |
260 | 5.54M | *pnConsumedBytes = nFormatWidth; |
261 | | |
262 | 5.59M | return nFormatWidth; |
263 | 5.59M | } |
264 | 5.59M | } |
265 | 2.33M | else |
266 | 2.33M | { |
267 | 2.33M | int nLength = 0; |
268 | 2.33M | int bAsciiField = TRUE; |
269 | 2.33M | int extraConsumedBytes = 0; |
270 | | |
271 | | /* We only check for the field terminator because of some buggy |
272 | | * datasets with missing format terminators. However, we have found |
273 | | * the field terminator and unit terminators are legal characters |
274 | | * within the fields of some extended datasets (such as JP34NC94.000). |
275 | | * So we don't check for the field terminator and unit terminators as |
276 | | * a single byte if the field appears to be multi-byte which we |
277 | | * establish by checking for the buffer ending with 0x1e 0x00 (a |
278 | | * two byte field terminator). |
279 | | * |
280 | | * In the case of S57, the subfield ATVL of the NATF field can be |
281 | | * encoded in lexical level 2 (see S57 specification, Edition 3.1, |
282 | | * paragraph 2.4 and 2.5). In that case the Unit Terminator and Field |
283 | | * Terminator are followed by the NULL character. |
284 | | * A better fix would be to read the NALL tag in the DSSI to check |
285 | | * that the lexical level is 2, instead of relying on the value of |
286 | | * the first byte as we are doing - but that is not information |
287 | | * that is available at the libiso8211 level (bug #1526) |
288 | | */ |
289 | | |
290 | | // If the whole field ends with 0x1e 0x00 then we assume this |
291 | | // field is a double byte character set. |
292 | 2.33M | if (nMaxBytes > 1 && |
293 | 2.30M | (pachSourceData[nMaxBytes - 2] == chFormatDelimiter || |
294 | 2.03M | pachSourceData[nMaxBytes - 2] == DDF_FIELD_TERMINATOR) && |
295 | 296k | pachSourceData[nMaxBytes - 1] == 0x00) |
296 | 3.45k | bAsciiField = FALSE; |
297 | | |
298 | | // if( !bAsciiField ) |
299 | | // CPLDebug( "ISO8211", "Non-ASCII field detected." ); |
300 | | |
301 | 27.2M | while (nLength < nMaxBytes) |
302 | 26.9M | { |
303 | 26.9M | if (bAsciiField) |
304 | 26.9M | { |
305 | 26.9M | if (pachSourceData[nLength] == chFormatDelimiter || |
306 | 25.0M | pachSourceData[nLength] == DDF_FIELD_TERMINATOR) |
307 | 2.08M | break; |
308 | 26.9M | } |
309 | 82.1k | else |
310 | 82.1k | { |
311 | 82.1k | if (nLength > 0 && |
312 | 78.6k | (pachSourceData[nLength - 1] == chFormatDelimiter || |
313 | 75.9k | pachSourceData[nLength - 1] == DDF_FIELD_TERMINATOR) && |
314 | 7.04k | pachSourceData[nLength] == 0) |
315 | 3.45k | { |
316 | | // Suck up the field terminator if one follows |
317 | | // or else it will be interpreted as a new subfield. |
318 | | // This is a pretty ugly counter-intuitive hack! |
319 | 3.45k | if (nLength + 1 < nMaxBytes && |
320 | 2.21k | pachSourceData[nLength + 1] == DDF_FIELD_TERMINATOR) |
321 | 489 | extraConsumedBytes++; |
322 | 3.45k | break; |
323 | 3.45k | } |
324 | 82.1k | } |
325 | | |
326 | 24.8M | nLength++; |
327 | 24.8M | } |
328 | | |
329 | 2.33M | if (pnConsumedBytes != nullptr) |
330 | 2.07M | { |
331 | 2.07M | if (nMaxBytes == 0) |
332 | 1.90k | *pnConsumedBytes = nLength + extraConsumedBytes; |
333 | 2.07M | else |
334 | 2.07M | *pnConsumedBytes = nLength + extraConsumedBytes + 1; |
335 | 2.07M | } |
336 | | |
337 | 2.33M | return nLength; |
338 | 2.33M | } |
339 | 7.93M | } |
340 | | |
341 | | /************************************************************************/ |
342 | | /* ExtractStringData() */ |
343 | | /************************************************************************/ |
344 | | |
345 | | /** |
346 | | * Extract a zero terminated string containing the data for this subfield. |
347 | | * Given a pointer to the data |
348 | | * for this subfield (from within a DDFRecord) this method will return the |
349 | | * data for this subfield. The number of bytes |
350 | | * consumed as part of this field can also be fetched. This number may |
351 | | * be one longer than the string length if there is a terminator character |
352 | | * used.<p> |
353 | | * |
354 | | * This function will return the raw binary data of a subfield for |
355 | | * types other than DDFString, including data past zero chars. This is |
356 | | * the standard way of extracting DDFBinaryString subfields for instance.<p> |
357 | | * |
358 | | * CAUTION: this method is not thread safe as it updates mutable member |
359 | | * variables. |
360 | | * |
361 | | * @param pachSourceData The pointer to the raw data for this field. This |
362 | | * may have come from DDFRecord::GetData(), taking into account skip factors |
363 | | * over previous subfields data. |
364 | | * @param nMaxBytes The maximum number of bytes that are accessible after |
365 | | * pachSourceData. |
366 | | * @param pnConsumedBytes Pointer to an integer into which the number of |
367 | | * bytes consumed by this field should be written. May be NULL to ignore. |
368 | | * This is used as a skip factor to increment pachSourceData to point to the |
369 | | * next subfields data. |
370 | | * |
371 | | * @return A pointer to a buffer containing the data for this field. The |
372 | | * returned pointer is to an internal buffer which is invalidated on the |
373 | | * next ExtractStringData() call on this DDFSubfieldDefn(). It should not |
374 | | * be freed by the application. |
375 | | * |
376 | | * @see ExtractIntData(), ExtractFloatData() |
377 | | */ |
378 | | |
379 | | const char *DDFSubfieldDefn::ExtractStringData(const char *pachSourceData, |
380 | | int nMaxBytes, |
381 | | int *pnConsumedBytes) const |
382 | | |
383 | 438k | { |
384 | 438k | const int nLength = |
385 | 438k | GetDataLength(pachSourceData, nMaxBytes, pnConsumedBytes); |
386 | | |
387 | 438k | osBuffer.assign(pachSourceData, pachSourceData + nLength); |
388 | | |
389 | 438k | return osBuffer.c_str(); |
390 | 438k | } |
391 | | |
392 | | /************************************************************************/ |
393 | | /* ExtractFloatData() */ |
394 | | /************************************************************************/ |
395 | | |
396 | | /** |
397 | | * Extract a subfield value as a float. Given a pointer to the data |
398 | | * for this subfield (from within a DDFRecord) this method will return the |
399 | | * floating point data for this subfield. The number of bytes |
400 | | * consumed as part of this field can also be fetched. This method may be |
401 | | * called for any type of subfield, and will return zero if the subfield is |
402 | | * not numeric. |
403 | | * |
404 | | * @param pachSourceData The pointer to the raw data for this field. This |
405 | | * may have come from DDFRecord::GetData(), taking into account skip factors |
406 | | * over previous subfields data. |
407 | | * @param nMaxBytes The maximum number of bytes that are accessible after |
408 | | * pachSourceData. |
409 | | * @param pnConsumedBytes Pointer to an integer into which the number of |
410 | | * bytes consumed by this field should be written. May be NULL to ignore. |
411 | | * This is used as a skip factor to increment pachSourceData to point to the |
412 | | * next subfields data. |
413 | | * |
414 | | * @return The subfield's numeric value (or zero if it isn't numeric). |
415 | | * |
416 | | * @see ExtractIntData(), ExtractStringData() |
417 | | */ |
418 | | |
419 | | double DDFSubfieldDefn::ExtractFloatData(const char *pachSourceData, |
420 | | int nMaxBytes, |
421 | | int *pnConsumedBytes) const |
422 | | |
423 | 58.4k | { |
424 | 58.4k | switch (osFormatString[0]) |
425 | 58.4k | { |
426 | 357 | case 'A': |
427 | 1.37k | case 'I': |
428 | 35.6k | case 'R': |
429 | 35.9k | case 'S': |
430 | 35.9k | case 'C': |
431 | 35.9k | return CPLAtof( |
432 | 35.9k | ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes)); |
433 | | |
434 | 4 | case 'B': |
435 | 22.5k | case 'b': |
436 | 22.5k | { |
437 | 22.5k | unsigned char abyData[8]; |
438 | 22.5k | void *pabyData = abyData; |
439 | | |
440 | 22.5k | if (nFormatWidth > nMaxBytes) |
441 | 2 | { |
442 | 2 | CPLError(CE_Warning, CPLE_AppDefined, |
443 | 2 | "Attempt to extract float subfield %s with format %s\n" |
444 | 2 | "failed as only %d bytes available. Using zero.", |
445 | 2 | osName.c_str(), osFormatString.c_str(), nMaxBytes); |
446 | 2 | return 0; |
447 | 2 | } |
448 | 22.5k | if (nFormatWidth > static_cast<int>(sizeof(abyData))) |
449 | 0 | { |
450 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
451 | 0 | "Format width %d too large", nFormatWidth); |
452 | 0 | return 0; |
453 | 0 | } |
454 | | |
455 | 22.5k | if (pnConsumedBytes != nullptr) |
456 | 22.5k | *pnConsumedBytes = nFormatWidth; |
457 | | |
458 | | // Byte swap the data if it isn't in machine native format. |
459 | | // In any event we copy it into our buffer to ensure it is |
460 | | // word aligned. |
461 | 22.5k | #ifdef CPL_LSB |
462 | 22.5k | if (osFormatString[0] == 'B') |
463 | | #else |
464 | | if (osFormatString[0] == 'b') |
465 | | #endif |
466 | 4 | { |
467 | 10 | for (int i = 0; i < nFormatWidth; i++) |
468 | 6 | abyData[nFormatWidth - i - 1] = pachSourceData[i]; |
469 | 4 | } |
470 | 22.5k | else |
471 | 22.5k | { |
472 | 22.5k | memcpy(abyData, pachSourceData, nFormatWidth); |
473 | 22.5k | } |
474 | | |
475 | | // Interpret the bytes of data. |
476 | 22.5k | switch (eBinaryFormat) |
477 | 22.5k | { |
478 | 0 | case UInt: |
479 | 0 | if (nFormatWidth == 1) |
480 | 0 | return abyData[0]; |
481 | 0 | else if (nFormatWidth == 2) |
482 | 0 | return *(static_cast<GUInt16 *>(pabyData)); |
483 | 0 | else if (nFormatWidth == 4) |
484 | 0 | return *(static_cast<GUInt32 *>(pabyData)); |
485 | 0 | else |
486 | 0 | { |
487 | | // CPLAssert( false ); |
488 | 0 | return 0.0; |
489 | 0 | } |
490 | | |
491 | 21 | case SInt: |
492 | 21 | if (nFormatWidth == 1) |
493 | 2 | return *(static_cast<signed char *>(pabyData)); |
494 | 19 | else if (nFormatWidth == 2) |
495 | 0 | return *(static_cast<GInt16 *>(pabyData)); |
496 | 19 | else if (nFormatWidth == 4) |
497 | 1 | return *(static_cast<GInt32 *>(pabyData)); |
498 | 18 | else |
499 | 18 | { |
500 | | // CPLAssert( false ); |
501 | 18 | return 0.0; |
502 | 18 | } |
503 | | |
504 | 22.5k | case FloatReal: |
505 | 22.5k | if (nFormatWidth == 4) |
506 | 0 | return static_cast<double>( |
507 | 0 | *(static_cast<float *>(pabyData))); |
508 | 22.5k | else if (nFormatWidth == 8) |
509 | 22.5k | return *(static_cast<double *>(pabyData)); |
510 | 0 | else |
511 | 0 | { |
512 | | // CPLAssert( false ); |
513 | 0 | return 0.0; |
514 | 0 | } |
515 | | |
516 | 0 | case NotBinary: |
517 | 1 | case FPReal: |
518 | 1 | case FloatComplex: |
519 | | // CPLAssert( false ); |
520 | 1 | return 0.0; |
521 | 22.5k | } |
522 | 0 | break; |
523 | | // end of 'b'/'B' case. |
524 | 22.5k | } |
525 | | |
526 | 0 | default: |
527 | | // CPLAssert( false ); |
528 | 0 | return 0.0; |
529 | 58.4k | } |
530 | | |
531 | | // CPLAssert( false ); |
532 | 0 | return 0.0; |
533 | 58.4k | } |
534 | | |
535 | | /************************************************************************/ |
536 | | /* ExtractIntData() */ |
537 | | /************************************************************************/ |
538 | | |
539 | | /** |
540 | | * Extract a subfield value as an integer. Given a pointer to the data |
541 | | * for this subfield (from within a DDFRecord) this method will return the |
542 | | * int data for this subfield. The number of bytes |
543 | | * consumed as part of this field can also be fetched. This method may be |
544 | | * called for any type of subfield, and will return zero if the subfield is |
545 | | * not numeric. |
546 | | * |
547 | | * @param pachSourceData The pointer to the raw data for this field. This |
548 | | * may have come from DDFRecord::GetData(), taking into account skip factors |
549 | | * over previous subfields data. |
550 | | * @param nMaxBytes The maximum number of bytes that are accessible after |
551 | | * pachSourceData. |
552 | | * @param pnConsumedBytes Pointer to an integer into which the number of |
553 | | * bytes consumed by this field should be written. May be NULL to ignore. |
554 | | * This is used as a skip factor to increment pachSourceData to point to the |
555 | | * next subfields data. |
556 | | * |
557 | | * @return The subfield's numeric value (or zero if it isn't numeric). |
558 | | * |
559 | | * @see ExtractFloatData(), ExtractStringData() |
560 | | */ |
561 | | |
562 | | int DDFSubfieldDefn::ExtractIntData(const char *pachSourceData, int nMaxBytes, |
563 | | int *pnConsumedBytes) const |
564 | | |
565 | 607k | { |
566 | 607k | switch (osFormatString[0]) |
567 | 607k | { |
568 | 6.61k | case 'A': |
569 | 93.2k | case 'I': |
570 | 93.4k | case 'R': |
571 | 94.2k | case 'S': |
572 | 94.2k | case 'C': |
573 | 94.2k | return atoi( |
574 | 94.2k | ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes)); |
575 | | |
576 | 15.7k | case 'B': |
577 | 513k | case 'b': |
578 | 513k | { |
579 | 513k | unsigned char abyData[8]; |
580 | 513k | void *pabyData = abyData; |
581 | | |
582 | 513k | if (nFormatWidth > nMaxBytes || |
583 | 507k | nFormatWidth >= static_cast<int>(sizeof(abyData))) |
584 | 5.28k | { |
585 | 5.28k | CPLError( |
586 | 5.28k | CE_Warning, CPLE_AppDefined, |
587 | 5.28k | "Attempt to extract int subfield %s with format %s\n" |
588 | 5.28k | "failed as only %d bytes available. Using zero.", |
589 | 5.28k | osName.c_str(), osFormatString.c_str(), |
590 | 5.28k | std::min(nMaxBytes, static_cast<int>(sizeof(abyData)))); |
591 | 5.28k | return 0; |
592 | 5.28k | } |
593 | | |
594 | 507k | if (pnConsumedBytes != nullptr) |
595 | 507k | *pnConsumedBytes = nFormatWidth; |
596 | | |
597 | | // Byte swap the data if it isn't in machine native format. |
598 | | // In any event we copy it into our buffer to ensure it is |
599 | | // word aligned. |
600 | 507k | #ifdef CPL_LSB |
601 | 507k | if (osFormatString[0] == 'B') |
602 | | #else |
603 | | if (osFormatString[0] == 'b') |
604 | | #endif |
605 | 10.5k | { |
606 | 50.7k | for (int i = 0; i < nFormatWidth; i++) |
607 | 40.1k | abyData[nFormatWidth - i - 1] = pachSourceData[i]; |
608 | 10.5k | } |
609 | 497k | else |
610 | 497k | { |
611 | 497k | memcpy(abyData, pachSourceData, nFormatWidth); |
612 | 497k | } |
613 | | |
614 | | // Interpret the bytes of data. |
615 | 507k | switch (eBinaryFormat) |
616 | 507k | { |
617 | 445k | case UInt: |
618 | 445k | if (nFormatWidth == 4) |
619 | 146k | return static_cast<int>( |
620 | 146k | *(static_cast<GUInt32 *>(pabyData))); |
621 | 299k | else if (nFormatWidth == 1) |
622 | 116k | return abyData[0]; |
623 | 182k | else if (nFormatWidth == 2) |
624 | 182k | return *(static_cast<GUInt16 *>(pabyData)); |
625 | 685 | else |
626 | 685 | { |
627 | | // CPLAssert( false ); |
628 | 685 | return 0; |
629 | 685 | } |
630 | | |
631 | 51.2k | case SInt: |
632 | 51.2k | if (nFormatWidth == 4) |
633 | 29.2k | return *(static_cast<GInt32 *>(pabyData)); |
634 | 21.9k | else if (nFormatWidth == 1) |
635 | 20.6k | return *(static_cast<signed char *>(pabyData)); |
636 | 1.30k | else if (nFormatWidth == 2) |
637 | 88 | return *(static_cast<GInt16 *>(pabyData)); |
638 | 1.21k | else |
639 | 1.21k | { |
640 | | // CPLAssert( false ); |
641 | 1.21k | return 0; |
642 | 1.21k | } |
643 | | |
644 | 9.50k | case FloatReal: |
645 | 9.50k | if (nFormatWidth == 4) |
646 | 9.42k | return static_cast<int>( |
647 | 9.42k | *(static_cast<float *>(pabyData))); |
648 | 85 | else if (nFormatWidth == 8) |
649 | 0 | return static_cast<int>( |
650 | 0 | *(static_cast<double *>(pabyData))); |
651 | 85 | else |
652 | 85 | { |
653 | | // CPLAssert( false ); |
654 | 85 | return 0; |
655 | 85 | } |
656 | | |
657 | 255 | case NotBinary: |
658 | 766 | case FPReal: |
659 | 1.27k | case FloatComplex: |
660 | | // CPLAssert( false ); |
661 | 1.27k | return 0; |
662 | 507k | } |
663 | 0 | break; |
664 | | // end of 'b'/'B' case. |
665 | 507k | } |
666 | | |
667 | 0 | default: |
668 | | // CPLAssert( false ); |
669 | 0 | return 0; |
670 | 607k | } |
671 | | |
672 | | // CPLAssert( false ); |
673 | 0 | return 0; |
674 | 607k | } |
675 | | |
676 | | /************************************************************************/ |
677 | | /* DumpData() */ |
678 | | /* */ |
679 | | /* Dump the instance data for this subfield from a data */ |
680 | | /* record. This fits into the output dump stream of a DDFField. */ |
681 | | /************************************************************************/ |
682 | | |
683 | | /** |
684 | | * Dump subfield value to debugging file. |
685 | | * |
686 | | * @param pachData Pointer to data for this subfield. |
687 | | * @param nMaxBytes Maximum number of bytes available in pachData. |
688 | | * @param fp File to write report to. |
689 | | */ |
690 | | |
691 | | void DDFSubfieldDefn::DumpData(const char *pachData, int nMaxBytes, |
692 | | FILE *fp) const |
693 | | |
694 | 0 | { |
695 | 0 | if (nMaxBytes < 0) |
696 | 0 | { |
697 | 0 | fprintf(fp, " Subfield `%s' = {invalid length}\n", osName.c_str()); |
698 | 0 | return; |
699 | 0 | } |
700 | 0 | if (eType == DDFFloat) |
701 | 0 | fprintf(fp, " Subfield `%s' = %f\n", osName.c_str(), |
702 | 0 | ExtractFloatData(pachData, nMaxBytes, nullptr)); |
703 | 0 | else if (eType == DDFInt) |
704 | 0 | fprintf(fp, " Subfield `%s' = %d\n", osName.c_str(), |
705 | 0 | ExtractIntData(pachData, nMaxBytes, nullptr)); |
706 | 0 | else if (eType == DDFBinaryString) |
707 | 0 | { |
708 | 0 | int nBytes = 0; |
709 | 0 | const GByte *pabyBString = reinterpret_cast<const GByte *>( |
710 | 0 | ExtractStringData(pachData, nMaxBytes, &nBytes)); |
711 | |
|
712 | 0 | fprintf(fp, " Subfield `%s' = 0x", osName.c_str()); |
713 | 0 | for (int i = 0; i < std::min(nBytes, 24); i++) |
714 | 0 | fprintf(fp, "%02X", pabyBString[i]); |
715 | |
|
716 | 0 | if (nBytes > 24) |
717 | 0 | fprintf(fp, "%s", "..."); |
718 | |
|
719 | 0 | fprintf(fp, "\n"); |
720 | 0 | } |
721 | 0 | else |
722 | 0 | fprintf(fp, " Subfield `%s' = `%s'\n", osName.c_str(), |
723 | 0 | ExtractStringData(pachData, nMaxBytes, nullptr)); |
724 | 0 | } |
725 | | |
726 | | /************************************************************************/ |
727 | | /* GetDefaultValue() */ |
728 | | /************************************************************************/ |
729 | | |
730 | | /** |
731 | | * Get default data. |
732 | | * |
733 | | * Returns the default subfield data contents for this subfield definition. |
734 | | * For variable length numbers this will normally be "0<unit-terminator>". |
735 | | * For variable length strings it will be "<unit-terminator>". For fixed |
736 | | * length numbers it is zero filled. For fixed length strings it is space |
737 | | * filled. For binary numbers it is binary zero filled. |
738 | | * |
739 | | * @param pachData the buffer into which the returned default will be placed. |
740 | | * May be NULL if just querying default size. |
741 | | * @param nBytesAvailable the size of pachData in bytes. |
742 | | * @param pnBytesUsed will receive the size of the subfield default data in |
743 | | * bytes. |
744 | | * |
745 | | * @return TRUE on success or FALSE on failure or if the passed buffer is too |
746 | | * small to hold the default. |
747 | | */ |
748 | | |
749 | | int DDFSubfieldDefn::GetDefaultValue(char *pachData, int nBytesAvailable, |
750 | | int *pnBytesUsed) const |
751 | | |
752 | 4.16k | { |
753 | 4.16k | int nDefaultSize; |
754 | | |
755 | 4.16k | if (!bIsVariable) |
756 | 4.16k | nDefaultSize = nFormatWidth; |
757 | 0 | else |
758 | 0 | nDefaultSize = 1; |
759 | | |
760 | 4.16k | if (pnBytesUsed != nullptr) |
761 | 4.16k | *pnBytesUsed = nDefaultSize; |
762 | | |
763 | 4.16k | if (pachData == nullptr) |
764 | 2.08k | return TRUE; |
765 | | |
766 | 2.08k | if (nBytesAvailable < nDefaultSize) |
767 | 0 | return FALSE; |
768 | | |
769 | 2.08k | if (bIsVariable) |
770 | 0 | { |
771 | 0 | pachData[0] = DDF_UNIT_TERMINATOR; |
772 | 0 | } |
773 | 2.08k | else |
774 | 2.08k | { |
775 | 2.08k | char chFillChar; |
776 | 2.08k | if (GetBinaryFormat() == NotBinary) |
777 | 0 | { |
778 | 0 | if (GetType() == DDFInt || GetType() == DDFFloat) |
779 | 0 | chFillChar = '0'; /* ASCII zero intended */ |
780 | 0 | else |
781 | 0 | chFillChar = ' '; |
782 | 0 | } |
783 | 2.08k | else |
784 | 2.08k | chFillChar = 0; |
785 | 2.08k | memset(pachData, chFillChar, nDefaultSize); |
786 | 2.08k | } |
787 | | |
788 | 2.08k | return TRUE; |
789 | 2.08k | } |
790 | | |
791 | | /************************************************************************/ |
792 | | /* FormatStringValue() */ |
793 | | /************************************************************************/ |
794 | | |
795 | | /** |
796 | | * Format string subfield value. |
797 | | * |
798 | | * Returns a buffer with the passed in string value reformatted in a way |
799 | | * suitable for storage in a DDFField for this subfield. |
800 | | */ |
801 | | |
802 | | int DDFSubfieldDefn::FormatStringValue(char *pachData, int nBytesAvailable, |
803 | | int *pnBytesUsed, const char *pszValue, |
804 | | int nValueLength) const |
805 | | |
806 | 0 | { |
807 | 0 | int nSize; |
808 | |
|
809 | 0 | if (nValueLength == -1) |
810 | 0 | nValueLength = static_cast<int>(strlen(pszValue)); |
811 | |
|
812 | 0 | if (bIsVariable) |
813 | 0 | { |
814 | 0 | nSize = nValueLength + 1; |
815 | 0 | } |
816 | 0 | else |
817 | 0 | { |
818 | 0 | nSize = nFormatWidth; |
819 | 0 | } |
820 | |
|
821 | 0 | if (pnBytesUsed != nullptr) |
822 | 0 | *pnBytesUsed = nSize; |
823 | |
|
824 | 0 | if (pachData == nullptr) |
825 | 0 | return TRUE; |
826 | | |
827 | 0 | if (nBytesAvailable < nSize) |
828 | 0 | return FALSE; |
829 | | |
830 | 0 | if (bIsVariable) |
831 | 0 | { |
832 | 0 | strncpy(pachData, pszValue, nSize - 1); |
833 | 0 | pachData[nSize - 1] = DDF_UNIT_TERMINATOR; |
834 | 0 | } |
835 | 0 | else |
836 | 0 | { |
837 | 0 | if (GetBinaryFormat() == NotBinary) |
838 | 0 | { |
839 | 0 | memset(pachData, ' ', nSize); |
840 | | // cppcheck-suppress redundantCopy |
841 | 0 | memcpy(pachData, pszValue, std::min(nValueLength, nSize)); |
842 | 0 | } |
843 | 0 | else |
844 | 0 | { |
845 | 0 | memset(pachData, 0, nSize); |
846 | | // cppcheck-suppress redundantCopy |
847 | 0 | memcpy(pachData, pszValue, std::min(nValueLength, nSize)); |
848 | 0 | } |
849 | 0 | } |
850 | |
|
851 | 0 | return TRUE; |
852 | 0 | } |
853 | | |
854 | | /************************************************************************/ |
855 | | /* FormatIntValue() */ |
856 | | /************************************************************************/ |
857 | | |
858 | | /** |
859 | | * Format int subfield value. |
860 | | * |
861 | | * Returns a buffer with the passed in int value reformatted in a way |
862 | | * suitable for storage in a DDFField for this subfield. |
863 | | */ |
864 | | |
865 | | int DDFSubfieldDefn::FormatIntValue(char *pachData, int nBytesAvailable, |
866 | | int *pnBytesUsed, int nNewValue) const |
867 | | |
868 | 9.23k | { |
869 | 9.23k | int nSize; |
870 | 9.23k | char szWork[30]; |
871 | | |
872 | 9.23k | snprintf(szWork, sizeof(szWork), "%d", nNewValue); |
873 | | |
874 | 9.23k | if (bIsVariable) |
875 | 0 | { |
876 | 0 | nSize = static_cast<int>(strlen(szWork)) + 1; |
877 | 0 | } |
878 | 9.23k | else |
879 | 9.23k | { |
880 | 9.23k | nSize = nFormatWidth; |
881 | | |
882 | 9.23k | if (GetBinaryFormat() == NotBinary && |
883 | 0 | static_cast<int>(strlen(szWork)) > nSize) |
884 | 0 | return FALSE; |
885 | 9.23k | } |
886 | | |
887 | 9.23k | if (pnBytesUsed != nullptr) |
888 | 4.63k | *pnBytesUsed = nSize; |
889 | | |
890 | 9.23k | if (pachData == nullptr) |
891 | 4.63k | return TRUE; |
892 | | |
893 | 4.59k | if (nBytesAvailable < nSize) |
894 | 0 | return FALSE; |
895 | | |
896 | 4.59k | if (bIsVariable) |
897 | 0 | { |
898 | 0 | memcpy(pachData, szWork, nSize - 1); |
899 | 0 | pachData[nSize - 1] = DDF_UNIT_TERMINATOR; |
900 | 0 | } |
901 | 4.59k | else |
902 | 4.59k | { |
903 | 4.59k | GUInt32 nMask = 0xff; |
904 | | |
905 | 4.59k | switch (GetBinaryFormat()) |
906 | 4.59k | { |
907 | 0 | case NotBinary: |
908 | 0 | { |
909 | 0 | constexpr char chFillChar = '0'; /* ASCII zero intended */ |
910 | 0 | const int nZeroFillCount = |
911 | 0 | nSize - static_cast<int>(strlen(szWork)); |
912 | 0 | for (int i = 0; i < nZeroFillCount; ++i) |
913 | 0 | pachData[i] = chFillChar; |
914 | 0 | memcpy(pachData + nZeroFillCount, szWork, strlen(szWork)); |
915 | 0 | break; |
916 | 0 | } |
917 | | |
918 | 4.59k | case UInt: |
919 | 4.59k | case SInt: |
920 | 14.1k | for (int i = 0; i < nFormatWidth; i++) |
921 | 9.58k | { |
922 | 9.58k | int iOut; |
923 | | |
924 | | // big endian required? |
925 | 9.58k | if (osFormatString[0] == 'B') |
926 | 0 | iOut = nFormatWidth - i - 1; |
927 | 9.58k | else |
928 | 9.58k | iOut = i; |
929 | | |
930 | 9.58k | pachData[iOut] = |
931 | 9.58k | static_cast<char>((nNewValue & nMask) >> (i * 8)); |
932 | 9.58k | nMask <<= 8; |
933 | 9.58k | } |
934 | 4.59k | break; |
935 | | |
936 | 0 | case FloatReal: |
937 | 0 | CPLAssert(false); |
938 | 0 | break; |
939 | | |
940 | 0 | default: |
941 | 0 | CPLAssert(false); |
942 | 0 | break; |
943 | 4.59k | } |
944 | 4.59k | } |
945 | | |
946 | 4.59k | return TRUE; |
947 | 4.59k | } |
948 | | |
949 | | /************************************************************************/ |
950 | | /* FormatFloatValue() */ |
951 | | /************************************************************************/ |
952 | | |
953 | | /** |
954 | | * Format float subfield value. |
955 | | * |
956 | | * Returns a buffer with the passed in float value reformatted in a way |
957 | | * suitable for storage in a DDFField for this subfield. |
958 | | */ |
959 | | |
960 | | int DDFSubfieldDefn::FormatFloatValue(char *pachData, int nBytesAvailable, |
961 | | int *pnBytesUsed, double dfNewValue) const |
962 | | |
963 | 0 | { |
964 | 0 | int nSize; |
965 | 0 | char szWork[120]; |
966 | |
|
967 | 0 | CPLsnprintf(szWork, sizeof(szWork), "%.16g", dfNewValue); |
968 | |
|
969 | 0 | if (bIsVariable) |
970 | 0 | { |
971 | 0 | nSize = static_cast<int>(strlen(szWork)) + 1; |
972 | 0 | } |
973 | 0 | else |
974 | 0 | { |
975 | 0 | nSize = nFormatWidth; |
976 | |
|
977 | 0 | if (GetBinaryFormat() == NotBinary && |
978 | 0 | static_cast<int>(strlen(szWork)) > nSize) |
979 | 0 | return FALSE; |
980 | 0 | } |
981 | | |
982 | 0 | if (pnBytesUsed != nullptr) |
983 | 0 | *pnBytesUsed = nSize; |
984 | |
|
985 | 0 | if (pachData == nullptr) |
986 | 0 | return TRUE; |
987 | | |
988 | 0 | if (nBytesAvailable < nSize) |
989 | 0 | return FALSE; |
990 | | |
991 | 0 | if (bIsVariable) |
992 | 0 | { |
993 | 0 | memcpy(pachData, szWork, nSize - 1); |
994 | 0 | pachData[nSize - 1] = DDF_UNIT_TERMINATOR; |
995 | 0 | } |
996 | 0 | else |
997 | 0 | { |
998 | 0 | if (GetBinaryFormat() == NotBinary) |
999 | 0 | { |
1000 | 0 | constexpr char chFillChar = '0'; /* ASCII zero intended */ |
1001 | 0 | const int nZeroFillCount = nSize - static_cast<int>(strlen(szWork)); |
1002 | 0 | for (int i = 0; i < nZeroFillCount; ++i) |
1003 | 0 | pachData[i] = chFillChar; |
1004 | 0 | memcpy(pachData + nZeroFillCount, szWork, strlen(szWork)); |
1005 | 0 | } |
1006 | 0 | else if (GetBinaryFormat() == FloatReal) |
1007 | 0 | { |
1008 | 0 | if (nFormatWidth == 8) |
1009 | 0 | { |
1010 | 0 | if (osFormatString[0] == 'B') |
1011 | 0 | { |
1012 | 0 | CPL_MSBPTR64(&dfNewValue); |
1013 | 0 | } |
1014 | 0 | else |
1015 | 0 | { |
1016 | 0 | CPL_LSBPTR64(&dfNewValue); |
1017 | 0 | } |
1018 | 0 | memcpy(pachData, &dfNewValue, sizeof(dfNewValue)); |
1019 | 0 | } |
1020 | 0 | else |
1021 | 0 | { |
1022 | 0 | CPLAssert(nFormatWidth == 4); |
1023 | 0 | float f = static_cast<float>(dfNewValue); |
1024 | 0 | if (osFormatString[0] == 'B') |
1025 | 0 | { |
1026 | 0 | CPL_MSBPTR32(&f); |
1027 | 0 | } |
1028 | 0 | else |
1029 | 0 | { |
1030 | 0 | CPL_LSBPTR32(&f); |
1031 | 0 | } |
1032 | 0 | memcpy(pachData, &f, sizeof(f)); |
1033 | 0 | } |
1034 | 0 | } |
1035 | 0 | else |
1036 | 0 | { |
1037 | 0 | CPLAssert(false); |
1038 | | /* implement me */ |
1039 | 0 | } |
1040 | 0 | } |
1041 | |
|
1042 | 0 | return TRUE; |
1043 | 0 | } |