Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_mapcoordblock.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABMAPCoordBlock class used to handle
7 : * reading/writing of the .MAP files' coordinate blocks
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999-2001, Daniel Morissette
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************/
16 :
17 : #include "cpl_port.h"
18 : #include "mitab.h"
19 :
20 : #include <climits>
21 : #include <cstddef>
22 : #include <algorithm>
23 :
24 : #include "mitab_priv.h"
25 : #include "mitab_utils.h"
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_vsi.h"
29 :
30 : /*=====================================================================
31 : * class TABMAPCoordBlock
32 : *====================================================================*/
33 :
34 : constexpr int MAP_COORD_HEADER_SIZE = 8;
35 :
36 : /**********************************************************************
37 : * TABMAPCoordBlock::TABMAPCoordBlock()
38 : *
39 : * Constructor.
40 : **********************************************************************/
41 246 : TABMAPCoordBlock::TABMAPCoordBlock(TABAccess eAccessMode /*= TABRead*/)
42 : : TABRawBinBlock(eAccessMode, TRUE), m_numDataBytes(0),
43 : m_nNextCoordBlock(0), m_numBlocksInChain(1), // Current block counts as 1
44 : m_nComprOrgX(0), m_nComprOrgY(0), m_nMinX(1000000000),
45 : m_nMinY(1000000000), m_nMaxX(-1000000000), m_nMaxY(-1000000000),
46 : m_poBlockManagerRef(nullptr), m_nTotalDataSize(0), m_nFeatureDataSize(0),
47 : m_nFeatureXMin(1000000000), m_nFeatureYMin(1000000000),
48 246 : m_nFeatureXMax(-1000000000), m_nFeatureYMax(-1000000000)
49 : {
50 246 : }
51 :
52 : /**********************************************************************
53 : * TABMAPCoordBlock::~TABMAPCoordBlock()
54 : *
55 : * Destructor.
56 : **********************************************************************/
57 492 : TABMAPCoordBlock::~TABMAPCoordBlock()
58 : {
59 492 : }
60 :
61 : /**********************************************************************
62 : * TABMAPCoordBlock::InitBlockFromData()
63 : *
64 : * Perform some initialization on the block after its binary data has
65 : * been set or changed (or loaded from a file).
66 : *
67 : * Returns 0 if successful or -1 if an error happened, in which case
68 : * CPLError() will have been called.
69 : **********************************************************************/
70 645 : int TABMAPCoordBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
71 : int nSizeUsed,
72 : GBool bMakeCopy /* = TRUE */,
73 : VSILFILE *fpSrc /* = NULL */,
74 : int nOffset /* = 0 */)
75 : {
76 : #ifdef DEBUG_VERBOSE
77 : CPLDebug("MITAB", "Instantiating COORD block to/from offset %d", nOffset);
78 : #endif
79 : /*-----------------------------------------------------------------
80 : * First of all, we must call the base class' InitBlockFromData()
81 : *----------------------------------------------------------------*/
82 645 : const int nStatus = TABRawBinBlock::InitBlockFromData(
83 : pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
84 645 : if (nStatus != 0)
85 0 : return nStatus;
86 :
87 : /*-----------------------------------------------------------------
88 : * Validate block type
89 : *----------------------------------------------------------------*/
90 645 : if (m_nBlockType != TABMAP_COORD_BLOCK)
91 : {
92 0 : CPLError(CE_Failure, CPLE_FileIO,
93 : "InitBlockFromData(): Invalid Block Type: got %d expected %d",
94 : m_nBlockType, TABMAP_COORD_BLOCK);
95 0 : CPLFree(m_pabyBuf);
96 0 : m_pabyBuf = nullptr;
97 0 : return -1;
98 : }
99 :
100 : /*-----------------------------------------------------------------
101 : * Init member variables
102 : *----------------------------------------------------------------*/
103 645 : GotoByteInBlock(0x002);
104 645 : m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
105 645 : if (m_numDataBytes < 0 ||
106 645 : m_numDataBytes + MAP_COORD_HEADER_SIZE > nBlockSize)
107 : {
108 0 : CPLError(CE_Failure, CPLE_FileIO,
109 : "TABMAPCoordBlock::InitBlockFromData(): m_numDataBytes=%d "
110 : "incompatible with block size %d",
111 : m_numDataBytes, nBlockSize);
112 0 : CPLFree(m_pabyBuf);
113 0 : m_pabyBuf = nullptr;
114 0 : return -1;
115 : }
116 :
117 645 : m_nNextCoordBlock = ReadInt32();
118 :
119 : // Set the real SizeUsed based on numDataBytes
120 645 : m_nSizeUsed = m_numDataBytes + MAP_COORD_HEADER_SIZE;
121 :
122 : /*-----------------------------------------------------------------
123 : * The read ptr is now located at the beginning of the data part.
124 : *----------------------------------------------------------------*/
125 645 : GotoByteInBlock(MAP_COORD_HEADER_SIZE);
126 :
127 645 : return 0;
128 : }
129 :
130 : /**********************************************************************
131 : * TABMAPCoordBlock::CommitToFile()
132 : *
133 : * Commit the current state of the binary block to the file to which
134 : * it has been previously attached.
135 : *
136 : * This method makes sure all values are properly set in the map object
137 : * block header and then calls TABRawBinBlock::CommitToFile() to do
138 : * the actual writing to disk.
139 : *
140 : * Returns 0 if successful or -1 if an error happened, in which case
141 : * CPLError() will have been called.
142 : **********************************************************************/
143 485 : int TABMAPCoordBlock::CommitToFile()
144 : {
145 485 : int nStatus = 0;
146 :
147 485 : CPLErrorReset();
148 :
149 485 : if (m_pabyBuf == nullptr)
150 : {
151 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
152 : "CommitToFile(): Block has not been initialized yet!");
153 0 : return -1;
154 : }
155 :
156 : /*-----------------------------------------------------------------
157 : * Nothing to do here if block has not been modified
158 : *----------------------------------------------------------------*/
159 485 : if (!m_bModified)
160 192 : return 0;
161 :
162 : /*-----------------------------------------------------------------
163 : * Make sure 8 bytes block header is up to date.
164 : *----------------------------------------------------------------*/
165 293 : GotoByteInBlock(0x000);
166 :
167 293 : WriteInt16(TABMAP_COORD_BLOCK); // Block type code
168 293 : CPLAssert(m_nSizeUsed >= MAP_COORD_HEADER_SIZE &&
169 : m_nSizeUsed < MAP_COORD_HEADER_SIZE + 32768);
170 293 : WriteInt16(static_cast<GInt16>(m_nSizeUsed -
171 : MAP_COORD_HEADER_SIZE)); // num. bytes used
172 293 : WriteInt32(m_nNextCoordBlock);
173 :
174 293 : if (CPLGetLastErrorType() == CE_Failure)
175 0 : nStatus = -1;
176 :
177 : /*-----------------------------------------------------------------
178 : * OK, call the base class to write the block to disk.
179 : *----------------------------------------------------------------*/
180 293 : if (nStatus == 0)
181 : {
182 : #ifdef DEBUG_VERBOSE
183 : CPLDebug("MITAB", "Committing COORD block to offset %d", m_nFileOffset);
184 : #endif
185 293 : nStatus = TABRawBinBlock::CommitToFile();
186 : }
187 :
188 293 : return nStatus;
189 : }
190 :
191 : /**********************************************************************
192 : * TABMAPCoordBlock::InitNewBlock()
193 : *
194 : * Initialize a newly created block so that it knows to which file it
195 : * is attached, its block size, etc . and then perform any specific
196 : * initialization for this block type, including writing a default
197 : * block header, etc. and leave the block ready to receive data.
198 : *
199 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
200 : * that puts the block in a stable state without loading any initial
201 : * data in it.
202 : *
203 : * Returns 0 if successful or -1 if an error happened, in which case
204 : * CPLError() will have been called.
205 : **********************************************************************/
206 149 : int TABMAPCoordBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
207 : int nFileOffset /* = 0*/)
208 : {
209 149 : CPLErrorReset();
210 : #ifdef DEBUG_VERBOSE
211 : CPLDebug("MITAB", "Instantiating new COORD block at offset %d",
212 : nFileOffset);
213 : #endif
214 : /*-----------------------------------------------------------------
215 : * Start with the default initialization
216 : *----------------------------------------------------------------*/
217 149 : if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
218 0 : return -1;
219 :
220 : /*-----------------------------------------------------------------
221 : * And then set default values for the block header.
222 : *
223 : * IMPORTANT: Do not reset m_nComprOrg here because its value needs to be
224 : * maintained between blocks in the same chain.
225 : *----------------------------------------------------------------*/
226 149 : m_nNextCoordBlock = 0;
227 :
228 149 : m_numDataBytes = 0;
229 :
230 : // m_nMin/Max are used to keep track of current block MBR
231 : // FeatureMin/Max should not be reset here since feature coords can
232 : // be split on several blocks
233 149 : m_nMinX = 1000000000;
234 149 : m_nMinY = 1000000000;
235 149 : m_nMaxX = -1000000000;
236 149 : m_nMaxY = -1000000000;
237 :
238 149 : if (m_eAccess != TABRead && nFileOffset != 0)
239 : {
240 127 : GotoByteInBlock(0x000);
241 :
242 127 : WriteInt16(TABMAP_COORD_BLOCK); // Block type code
243 127 : WriteInt16(0); // num. bytes used, excluding header
244 127 : WriteInt32(0); // Pointer to next coord block
245 : }
246 :
247 149 : if (CPLGetLastErrorType() == CE_Failure)
248 0 : return -1;
249 :
250 149 : return 0;
251 : }
252 :
253 : /**********************************************************************
254 : * TABMAPObjectBlock::SetNextCoordBlock()
255 : *
256 : * Set the address (offset from beginning of file) of the coord. block
257 : * that follows the current one.
258 : **********************************************************************/
259 68 : void TABMAPCoordBlock::SetNextCoordBlock(GInt32 nNextCoordBlockAddress)
260 : {
261 68 : m_nNextCoordBlock = nNextCoordBlockAddress;
262 68 : m_bModified = TRUE;
263 68 : }
264 :
265 : /**********************************************************************
266 : * TABMAPObjectBlock::SetComprCoordOrigin()
267 : *
268 : * Set the Compressed integer coordinates space origin to be used when
269 : * reading compressed coordinates using ReadIntCoord().
270 : **********************************************************************/
271 2554 : void TABMAPCoordBlock::SetComprCoordOrigin(GInt32 nX, GInt32 nY)
272 : {
273 2554 : m_nComprOrgX = nX;
274 2554 : m_nComprOrgY = nY;
275 2554 : }
276 :
277 : /**********************************************************************
278 : * TABMAPObjectBlock::ReadIntCoord()
279 : *
280 : * Read the next pair of integer coordinates value from the block, and
281 : * apply the translation relative to the origin of the coord. space
282 : * previously set using SetComprCoordOrigin() if bCompressed=TRUE.
283 : *
284 : * This means that the returned coordinates are always absolute integer
285 : * coordinates, even when the source coords are in compressed form.
286 : *
287 : * Returns 0 if successful or -1 if an error happened, in which case
288 : * CPLError() will have been called.
289 : **********************************************************************/
290 11431 : int TABMAPCoordBlock::ReadIntCoord(GBool bCompressed, GInt32 &nX, GInt32 &nY)
291 : {
292 11431 : if (bCompressed)
293 : {
294 6321 : nX = ReadInt16();
295 6321 : nY = ReadInt16();
296 6321 : TABSaturatedAdd(nX, m_nComprOrgX);
297 6321 : TABSaturatedAdd(nY, m_nComprOrgY);
298 : }
299 : else
300 : {
301 5110 : nX = ReadInt32();
302 5110 : nY = ReadInt32();
303 : }
304 :
305 11431 : if (CPLGetLastErrorType() == CE_Failure)
306 0 : return -1;
307 :
308 11431 : return 0;
309 : }
310 :
311 : /**********************************************************************
312 : * TABMAPObjectBlock::ReadIntCoords()
313 : *
314 : * Read the specified number of pairs of X,Y integer coordinates values
315 : * from the block, and apply the translation relative to the origin of
316 : * the coord. space previously set using SetComprCoordOrigin() if
317 : * bCompressed=TRUE.
318 : *
319 : * This means that the returned coordinates are always absolute integer
320 : * coordinates, even when the source coords are in compressed form.
321 : *
322 : * panXY should point to an array big enough to receive the specified
323 : * number of coordinates.
324 : *
325 : * Returns 0 if successful or -1 if an error happened, in which case
326 : * CPLError() will have been called.
327 : **********************************************************************/
328 524 : int TABMAPCoordBlock::ReadIntCoords(GBool bCompressed, int numCoordPairs,
329 : GInt32 *panXY)
330 : {
331 524 : int i, numValues = numCoordPairs * 2;
332 :
333 524 : if (bCompressed)
334 : {
335 9987 : for (i = 0; i < numValues; i += 2)
336 : {
337 9518 : panXY[i] = ReadInt16();
338 9518 : panXY[i + 1] = ReadInt16();
339 9518 : TABSaturatedAdd(panXY[i], m_nComprOrgX);
340 9518 : TABSaturatedAdd(panXY[i + 1], m_nComprOrgY);
341 9518 : if (CPLGetLastErrorType() == CE_Failure)
342 0 : return -1;
343 : }
344 : }
345 : else
346 : {
347 4040 : for (i = 0; i < numValues; i += 2)
348 : {
349 3985 : panXY[i] = ReadInt32();
350 3985 : panXY[i + 1] = ReadInt32();
351 3985 : if (CPLGetLastErrorType() == CE_Failure)
352 0 : return -1;
353 : }
354 : }
355 :
356 524 : return 0;
357 : }
358 :
359 : /**********************************************************************
360 : * TABMAPObjectBlock::ReadCoordSecHdrs()
361 : *
362 : * Read a set of coordinate section headers for PLINE MULTIPLE or REGIONs
363 : * and store the result in the array of structures pasHdrs[]. It is assumed
364 : * that pasHdrs points to an allocated array of at least numSections
365 : * TABMAPCoordSecHdr structures.
366 : *
367 : * The function will also set the values of numVerticesTotal to the
368 : * total number of coordinates in the object (the sum of all sections
369 : * headers read).
370 : *
371 : * At the end of the call, this TABMAPCoordBlock object will be located
372 : * at the beginning of the coordinate data.
373 : *
374 : * In V450 the numVertices is stored on an int32 instead of an int16
375 : *
376 : * In V800 the numHoles is stored on an int32 instead of an int16
377 : *
378 : * IMPORTANT: This function makes the assumption that coordinates for all
379 : * the sections are grouped together immediately after the
380 : * last section header block (i.e. that the coord. data is not
381 : * located all over the place). If it is not the case then
382 : * an error will be produced and the code to read region and
383 : * multipline objects will have to be updated.
384 : *
385 : * Returns 0 if successful or -1 if an error happened, in which case
386 : * CPLError() will have been called.
387 : **********************************************************************/
388 524 : int TABMAPCoordBlock::ReadCoordSecHdrs(GBool bCompressed, int nVersion,
389 : int numSections,
390 : TABMAPCoordSecHdr *pasHdrs,
391 : GInt32 &numVerticesTotal)
392 : {
393 524 : CPLErrorReset();
394 :
395 : /*-------------------------------------------------------------
396 : * Note about header+vertices size vs compressed coordinates:
397 : * The uncompressed header sections are actually 16 bytes, but the
398 : * offset calculations are based on prior decompression of the
399 : * coordinates. Our coordinate offset calculations have
400 : * to take this fact into account.
401 : * Also, V450 header section uses int32 instead of int16 for numVertices
402 : * and we add another 2 bytes to align with a 4 bytes boundary.
403 : * V800 header section uses int32 for numHoles but there is no need
404 : * for the 2 alignment bytes so the size is the same as V450
405 : *------------------------------------------------------------*/
406 524 : const int nSectionSize = (nVersion >= 450) ? 28 : 24;
407 524 : if (numSections > INT_MAX / nSectionSize)
408 : {
409 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid numSections");
410 0 : return -1;
411 : }
412 524 : const int nTotalHdrSizeUncompressed = nSectionSize * numSections;
413 :
414 524 : const int nVertexSize =
415 : bCompressed ? 2 * sizeof(GUInt16) : 2 * sizeof(GUInt32);
416 524 : numVerticesTotal = 0;
417 :
418 1054 : for (int i = 0; i < numSections; i++)
419 : {
420 : /*-------------------------------------------------------------
421 : * Read the coord. section header blocks
422 : *------------------------------------------------------------*/
423 : #ifdef TABDUMP
424 : int nHdrAddress = GetCurAddress();
425 : #endif
426 530 : if (nVersion >= 450)
427 8 : pasHdrs[i].numVertices = ReadInt32();
428 : else
429 522 : pasHdrs[i].numVertices = ReadInt16();
430 :
431 530 : if (pasHdrs[i].numVertices < 0 ||
432 530 : pasHdrs[i].numVertices > INT_MAX / nVertexSize)
433 : {
434 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
435 : "Invalid number of vertices for section %d", i);
436 0 : return -1;
437 : }
438 530 : if (nVersion >= 800)
439 0 : pasHdrs[i].numHoles = ReadInt32();
440 : else
441 530 : pasHdrs[i].numHoles = ReadInt16();
442 530 : if (pasHdrs[i].numHoles < 0)
443 : {
444 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
445 : "Invalid number of holes for section %d", i);
446 0 : return -1;
447 : }
448 530 : ReadIntCoord(bCompressed, pasHdrs[i].nXMin, pasHdrs[i].nYMin);
449 530 : ReadIntCoord(bCompressed, pasHdrs[i].nXMax, pasHdrs[i].nYMax);
450 530 : pasHdrs[i].nDataOffset = ReadInt32();
451 530 : if (pasHdrs[i].nDataOffset < nTotalHdrSizeUncompressed)
452 : {
453 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
454 : "Invalid data offset for section %d", i);
455 0 : return -1;
456 : }
457 :
458 530 : if (CPLGetLastErrorType() != 0)
459 0 : return -1;
460 :
461 530 : if (numVerticesTotal > INT_MAX / nVertexSize - pasHdrs[i].numVertices)
462 : {
463 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
464 : "Invalid number of vertices for section %d", i);
465 0 : return -1;
466 : }
467 530 : numVerticesTotal += pasHdrs[i].numVertices;
468 :
469 530 : pasHdrs[i].nVertexOffset =
470 530 : (pasHdrs[i].nDataOffset - nTotalHdrSizeUncompressed) / 8;
471 : #ifdef TABDUMP
472 : printf("READING pasHdrs[%d] @ %d = \n" /*ok*/
473 : " { numVertices = %d, numHoles = %d, \n"
474 : " nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n"
475 : " nDataOffset=%d, nVertexOffset=%d }\n",
476 : i, nHdrAddress, pasHdrs[i].numVertices, pasHdrs[i].numHoles,
477 : pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax,
478 : pasHdrs[i].nYMax, pasHdrs[i].nDataOffset,
479 : pasHdrs[i].nVertexOffset);
480 : printf(" dX = %d, dY = %d (center = %d , %d)\n", /*ok*/
481 : pasHdrs[i].nXMax - pasHdrs[i].nXMin,
482 : pasHdrs[i].nYMax - pasHdrs[i].nYMin, m_nComprOrgX, m_nComprOrgY);
483 : #endif
484 : }
485 :
486 1054 : for (int i = 0; i < numSections; i++)
487 : {
488 : /*-------------------------------------------------------------
489 : * Make sure all coordinates are grouped together
490 : * (Well... at least check that all the vertex indices are enclosed
491 : * inside the [0..numVerticesTotal] range.)
492 : *------------------------------------------------------------*/
493 530 : if (pasHdrs[i].nVertexOffset < 0 ||
494 530 : pasHdrs[i].nVertexOffset > INT_MAX - pasHdrs[i].numVertices ||
495 530 : (pasHdrs[i].nVertexOffset + pasHdrs[i].numVertices) >
496 530 : numVerticesTotal)
497 : {
498 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
499 : "Unsupported case or corrupt file: MULTIPLINE/REGION "
500 : "object vertices do not appear to be grouped together.");
501 0 : return -1;
502 : }
503 : }
504 :
505 524 : return 0;
506 : }
507 :
508 : /**********************************************************************
509 : * TABMAPObjectBlock::WriteCoordSecHdrs()
510 : *
511 : * Write a set of coordinate section headers for PLINE MULTIPLE or REGIONs.
512 : * pasHdrs should point to an array of numSections TABMAPCoordSecHdr
513 : * structures that have been properly initialized.
514 : *
515 : * In V450 the numVertices is stored on an int32 instead of an int16
516 : *
517 : * In V800 the numHoles is stored on an int32 instead of an int16
518 : *
519 : * At the end of the call, this TABMAPCoordBlock object will be ready to
520 : * receive the coordinate data.
521 : *
522 : * Returns 0 if successful or -1 if an error happened, in which case
523 : * CPLError() will have been called.
524 : **********************************************************************/
525 96 : int TABMAPCoordBlock::WriteCoordSecHdrs(int nVersion, int numSections,
526 : TABMAPCoordSecHdr *pasHdrs,
527 : GBool bCompressed /*=FALSE*/)
528 : {
529 96 : CPLErrorReset();
530 :
531 194 : for (int i = 0; i < numSections; i++)
532 : {
533 : /*-------------------------------------------------------------
534 : * Write the coord. section header blocks
535 : *------------------------------------------------------------*/
536 : #ifdef TABDUMP
537 : printf("WRITING pasHdrs[%d] @ %d = \n" /*ok*/
538 : " { numVertices = %d, numHoles = %d, \n"
539 : " nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n"
540 : " nDataOffset=%d, nVertexOffset=%d }\n",
541 : i, GetCurAddress(), pasHdrs[i].numVertices, pasHdrs[i].numHoles,
542 : pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax,
543 : pasHdrs[i].nYMax, pasHdrs[i].nDataOffset,
544 : pasHdrs[i].nVertexOffset);
545 : printf(" dX = %d, dY = %d (center = %d , %d)\n", /*ok*/
546 : pasHdrs[i].nXMax - pasHdrs[i].nXMin,
547 : pasHdrs[i].nYMax - pasHdrs[i].nYMin, m_nComprOrgX, m_nComprOrgY);
548 : #endif
549 :
550 98 : if (nVersion >= 450)
551 0 : WriteInt32(pasHdrs[i].numVertices);
552 : else
553 98 : WriteInt16(static_cast<GInt16>(pasHdrs[i].numVertices));
554 98 : if (nVersion >= 800)
555 0 : WriteInt32(pasHdrs[i].numHoles);
556 : else
557 98 : WriteInt16(static_cast<GInt16>(pasHdrs[i].numHoles));
558 98 : WriteIntCoord(pasHdrs[i].nXMin, pasHdrs[i].nYMin, bCompressed);
559 98 : WriteIntCoord(pasHdrs[i].nXMax, pasHdrs[i].nYMax, bCompressed);
560 98 : WriteInt32(pasHdrs[i].nDataOffset);
561 :
562 98 : if (CPLGetLastErrorType() == CE_Failure)
563 0 : return -1;
564 : }
565 :
566 96 : return 0;
567 : }
568 :
569 : /**********************************************************************
570 : * TABMAPCoordBlock::WriteIntCoord()
571 : *
572 : * Write a pair of integer coordinates values to the current position in the
573 : * the block.
574 : *
575 : * Returns 0 if successful or -1 if an error happened, in which case
576 : * CPLError() will have been called.
577 : **********************************************************************/
578 :
579 7475 : int TABMAPCoordBlock::WriteIntCoord(GInt32 nX, GInt32 nY,
580 : GBool bCompressed /*=FALSE*/)
581 : {
582 :
583 11355 : if ((!bCompressed && (WriteInt32(nX) != 0 || WriteInt32(nY) != 0)) ||
584 3880 : (bCompressed && (WriteInt16(TABInt16Diff(nX, m_nComprOrgX)) != 0 ||
585 3880 : WriteInt16(TABInt16Diff(nY, m_nComprOrgY)) != 0)))
586 : {
587 0 : return -1;
588 : }
589 :
590 : /*-----------------------------------------------------------------
591 : * Update block MBR
592 : *----------------------------------------------------------------*/
593 : //__TODO__ Do we still need to track the block MBR???
594 7475 : if (nX < m_nMinX)
595 639 : m_nMinX = nX;
596 7475 : if (nX > m_nMaxX)
597 3928 : m_nMaxX = nX;
598 :
599 7475 : if (nY < m_nMinY)
600 670 : m_nMinY = nY;
601 7475 : if (nY > m_nMaxY)
602 3557 : m_nMaxY = nY;
603 :
604 : /*-------------------------------------------------------------
605 : * Also keep track of current feature MBR.
606 : *------------------------------------------------------------*/
607 7475 : if (nX < m_nFeatureXMin)
608 719 : m_nFeatureXMin = nX;
609 7475 : if (nX > m_nFeatureXMax)
610 4303 : m_nFeatureXMax = nX;
611 :
612 7475 : if (nY < m_nFeatureYMin)
613 627 : m_nFeatureYMin = nY;
614 7475 : if (nY > m_nFeatureYMax)
615 2723 : m_nFeatureYMax = nY;
616 :
617 7475 : return 0;
618 : }
619 :
620 : /**********************************************************************
621 : * TABMAPCoordBlock::SetMAPBlockManagerRef()
622 : *
623 : * Pass a reference to the block manager object for the file this
624 : * block belongs to. The block manager will be used by this object
625 : * when it needs to automatically allocate a new block.
626 : **********************************************************************/
627 246 : void TABMAPCoordBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
628 : {
629 246 : m_poBlockManagerRef = poBlockMgr;
630 246 : }
631 :
632 : /**********************************************************************
633 : * TABMAPCoordBlock::ReadBytes()
634 : *
635 : * Cover function for TABRawBinBlock::ReadBytes() that will automagically
636 : * load the next coordinate block in the chain before reading the
637 : * requested bytes if we are at the end of the current block and if
638 : * m_nNextCoordBlock is a valid block.
639 : *
640 : * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
641 : * work:
642 : * Copy the number of bytes from the data block's internal buffer to
643 : * the user's buffer pointed by pabyDstBuf.
644 : *
645 : * Passing pabyDstBuf = NULL will only move the read pointer by the
646 : * specified number of bytes as if the copy had happened... but it
647 : * won't crash.
648 : *
649 : * Returns 0 if successful or -1 if an error happened, in which case
650 : * CPLError() will have been called.
651 : **********************************************************************/
652 52828 : int TABMAPCoordBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
653 : {
654 :
655 52828 : if (m_pabyBuf && m_nCurPos >= (m_numDataBytes + MAP_COORD_HEADER_SIZE) &&
656 328 : m_nNextCoordBlock > 0)
657 : {
658 : // We're at end of current block... advance to next block.
659 191 : int nStatus = GotoByteInFile(m_nNextCoordBlock, TRUE);
660 :
661 191 : if (nStatus != 0)
662 : {
663 : // Failed.... an error has already been reported.
664 0 : return nStatus;
665 : }
666 :
667 191 : GotoByteInBlock(MAP_COORD_HEADER_SIZE); // Move pointer past header
668 191 : m_numBlocksInChain++;
669 : }
670 :
671 52828 : if (m_pabyBuf && m_nCurPos < (m_numDataBytes + MAP_COORD_HEADER_SIZE) &&
672 52691 : m_nCurPos + numBytes > (m_numDataBytes + MAP_COORD_HEADER_SIZE) &&
673 0 : m_nNextCoordBlock > 0)
674 : {
675 : // Data overlaps on more than one block
676 : // Read until end of this block and then recursively call ReadBytes()
677 : // for the rest.
678 0 : int numBytesInThisBlock =
679 0 : (m_numDataBytes + MAP_COORD_HEADER_SIZE) - m_nCurPos;
680 : int nStatus =
681 0 : TABRawBinBlock::ReadBytes(numBytesInThisBlock, pabyDstBuf);
682 0 : if (nStatus == 0)
683 : nStatus =
684 0 : TABMAPCoordBlock::ReadBytes(numBytes - numBytesInThisBlock,
685 0 : pabyDstBuf + numBytesInThisBlock);
686 0 : return nStatus;
687 : }
688 :
689 52828 : return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
690 : }
691 :
692 : /**********************************************************************
693 : * TABMAPCoordBlock::WriteBytes()
694 : *
695 : * Cover function for TABRawBinBlock::WriteBytes() that will automagically
696 : * CommitToFile() the current block and create a new one if we are at
697 : * the end of the current block.
698 : *
699 : * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
700 : * work.
701 : *
702 : * Passing pabySrcBuf = NULL will only move the write pointer by the
703 : * specified number of bytes as if the copy had happened... but it
704 : * won't crash.
705 : *
706 : * Returns 0 if successful or -1 if an error happened, in which case
707 : * CPLError() will have been called.
708 : **********************************************************************/
709 16531 : int TABMAPCoordBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
710 : {
711 16531 : if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
712 : {
713 0 : CPLError(CE_Failure, CPLE_AppDefined,
714 : "WriteBytes(): Block does not support write operations.");
715 0 : return -1;
716 : }
717 :
718 16531 : if (m_poBlockManagerRef && (m_nBlockSize - m_nCurPos) < nBytesToWrite)
719 : {
720 68 : if (nBytesToWrite <= (m_nBlockSize - MAP_COORD_HEADER_SIZE))
721 : {
722 : // Data won't fit in this block but can fit inside a single
723 : // block, so we'll allocate a new block for it. This will
724 : // prevent us from overlapping coordinate values on 2 blocks, but
725 : // still allows strings longer than one block (see 'else' below).
726 : //
727 :
728 68 : if (m_nNextCoordBlock != 0)
729 : {
730 : // We're in read/write mode and there is already an allocated
731 : // block following this one in the chain ... just reload it
732 : // and continue writing to it
733 :
734 0 : CPLAssert(m_eAccess == TABReadWrite);
735 :
736 0 : if (CommitToFile() != 0 ||
737 0 : ReadFromFile(m_fp, m_nNextCoordBlock, m_nBlockSize) != 0)
738 : {
739 : // An error message should have already been reported.
740 0 : return -1;
741 : }
742 : }
743 : else
744 : {
745 : // Need to alloc a new block.
746 :
747 : int nNewBlockOffset =
748 68 : m_poBlockManagerRef->AllocNewBlock("COORD");
749 68 : SetNextCoordBlock(nNewBlockOffset);
750 :
751 136 : if (CommitToFile() != 0 ||
752 68 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
753 : {
754 : // An error message should have already been reported.
755 0 : return -1;
756 : }
757 :
758 68 : m_numBlocksInChain++;
759 : }
760 : }
761 : else
762 : {
763 : // Data to write is longer than one block... so we'll have to
764 : // split it over multiple block through multiple calls.
765 : //
766 0 : int nStatus = 0;
767 0 : while (nStatus == 0 && nBytesToWrite > 0)
768 : {
769 0 : int nBytes = m_nBlockSize - MAP_COORD_HEADER_SIZE;
770 0 : if ((m_nBlockSize - m_nCurPos) > 0)
771 : {
772 : // Use free room in current block
773 0 : nBytes = (m_nBlockSize - m_nCurPos);
774 : }
775 :
776 0 : nBytes = std::min(nBytes, nBytesToWrite);
777 :
778 : // The following call will result in a new block being
779 : // allocated in the if() block above.
780 0 : nStatus = TABMAPCoordBlock::WriteBytes(nBytes, pabySrcBuf);
781 :
782 0 : nBytesToWrite -= nBytes;
783 0 : pabySrcBuf += nBytes;
784 : }
785 0 : return nStatus;
786 : }
787 : }
788 :
789 16531 : if (m_nCurPos >= MAP_COORD_HEADER_SIZE)
790 : {
791 : // Keep track of Coordinate data... this means ignore header bytes
792 : // that could be written.
793 15247 : m_nTotalDataSize += nBytesToWrite;
794 15247 : m_nFeatureDataSize += nBytesToWrite;
795 : }
796 :
797 16531 : return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
798 : }
799 :
800 : /**********************************************************************
801 : * TABMAPObjectBlock::SeekEnd()
802 : *
803 : * Move read/write pointer to end of used part of the block
804 : **********************************************************************/
805 475 : void TABMAPCoordBlock::SeekEnd()
806 : {
807 475 : m_nCurPos = m_nSizeUsed;
808 475 : }
809 :
810 : /**********************************************************************
811 : * TABMAPCoordBlock::StartNewFeature()
812 : *
813 : * Reset all member vars that are used to keep track of data size
814 : * and MBR for the current feature. This is info is not needed by
815 : * the coord blocks themselves, but it helps a lot the callers to
816 : * have this class take care of that for them.
817 : *
818 : * See Also: GetFeatureDataSize() and GetFeatureMBR()
819 : **********************************************************************/
820 475 : void TABMAPCoordBlock::StartNewFeature()
821 : {
822 475 : m_nFeatureDataSize = 0;
823 :
824 475 : m_nFeatureXMin = 1000000000;
825 475 : m_nFeatureYMin = 1000000000;
826 475 : m_nFeatureXMax = -1000000000;
827 475 : m_nFeatureYMax = -1000000000;
828 475 : }
829 :
830 : /**********************************************************************
831 : * TABMAPCoordBlock::GetFeatureMBR()
832 : *
833 : * Return the MBR of all the coords written using WriteIntCoord() since
834 : * the last call to StartNewFeature().
835 : **********************************************************************/
836 0 : void TABMAPCoordBlock::GetFeatureMBR(GInt32 &nXMin, GInt32 &nYMin,
837 : GInt32 &nXMax, GInt32 &nYMax)
838 : {
839 0 : nXMin = m_nFeatureXMin;
840 0 : nYMin = m_nFeatureYMin;
841 0 : nXMax = m_nFeatureXMax;
842 0 : nYMax = m_nFeatureYMax;
843 0 : }
844 :
845 : /**********************************************************************
846 : * TABMAPCoordBlock::Dump()
847 : *
848 : * Dump block contents... available only in DEBUG mode.
849 : **********************************************************************/
850 : #ifdef DEBUG
851 :
852 0 : void TABMAPCoordBlock::Dump(FILE *fpOut /*=NULL*/)
853 : {
854 0 : if (fpOut == nullptr)
855 0 : fpOut = stdout;
856 :
857 0 : fprintf(fpOut, "----- TABMAPCoordBlock::Dump() -----\n");
858 0 : if (m_pabyBuf == nullptr)
859 : {
860 0 : fprintf(fpOut, "Block has not been initialized yet.");
861 : }
862 : else
863 : {
864 0 : fprintf(fpOut, "Coordinate Block (type %d) at offset %d.\n",
865 : m_nBlockType, m_nFileOffset);
866 0 : fprintf(fpOut, " m_numDataBytes = %d\n", m_numDataBytes);
867 0 : fprintf(fpOut, " m_nNextCoordBlock = %d\n", m_nNextCoordBlock);
868 : }
869 :
870 0 : fflush(fpOut);
871 0 : }
872 :
873 : #endif // DEBUG
|