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