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