1 /**********************************************************************
2 * $Id: mitab_rawbinblock.cpp,v 1.11 2007-06-11 14:40:03 dmorissette Exp $
3 *
4 * Name: mitab_rawbinblock.cpp
5 * Project: MapInfo TAB Read/Write library
6 * Language: C++
7 * Purpose: Implementation of the TABRawBinBlock class used to handle
8 * reading/writing blocks in the .MAP files
9 * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 *
11 **********************************************************************
12 * Copyright (c) 1999, 2000, Daniel Morissette
13 * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a
16 * copy of this software and associated documentation files (the "Software"),
17 * to deal in the Software without restriction, including without limitation
18 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 * and/or sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included
23 * in all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31 * DEALINGS IN THE SOFTWARE.
32 **********************************************************************
33 *
34 * $Log: mitab_rawbinblock.cpp,v $
35 * Revision 1.11 2007-06-11 14:40:03 dmorissette
36 * Fixed another issue related to attempting to read past EOF while writing
37 * collections (bug 1657)
38 *
39 * Revision 1.10 2007/02/22 18:35:53 dmorissette
40 * Fixed problem writing collections where MITAB was sometimes trying to
41 * read past EOF in write mode (bug 1657).
42 *
43 * Revision 1.9 2006/11/28 18:49:08 dmorissette
44 * Completed changes to split TABMAPObjectBlocks properly and produce an
45 * optimal spatial index (bug 1585)
46 *
47 * Revision 1.8 2005/10/06 19:15:31 dmorissette
48 * Collections: added support for reading/writing pen/brush/symbol ids and
49 * for writing collection objects to .TAB/.MAP (bug 1126)
50 *
51 * Revision 1.7 2004/12/01 18:25:03 dmorissette
52 * Fixed potential memory leaks in error conditions (bug 881)
53 *
54 * Revision 1.6 2004/06/30 20:29:04 dmorissette
55 * Fixed refs to old address danmo@videotron.ca
56 *
57 * Revision 1.5 2000/02/28 17:06:06 daniel
58 * Added m_bModified flag
59 *
60 * Revision 1.4 2000/01/15 22:30:45 daniel
61 * Switch to MIT/X-Consortium OpenSource license
62 *
63 * Revision 1.3 1999/09/26 14:59:37 daniel
64 * Implemented write support
65 *
66 * Revision 1.2 1999/09/16 02:39:17 daniel
67 * Completed read support for most feature types
68 *
69 * Revision 1.1 1999/07/12 04:18:25 daniel
70 * Initial checkin
71 *
72 **********************************************************************/
73
74 #include "mitab.h"
75
76 /*=====================================================================
77 * class TABRawBinBlock
78 *====================================================================*/
79
80
81 /**********************************************************************
82 * TABRawBinBlock::TABRawBinBlock()
83 *
84 * Constructor.
85 **********************************************************************/
TABRawBinBlock(TABAccess eAccessMode,GBool bHardBlockSize)86 TABRawBinBlock::TABRawBinBlock(TABAccess eAccessMode /*= TABRead*/,
87 GBool bHardBlockSize /*= TRUE*/)
88 {
89 m_fp = NULL;
90 m_pabyBuf = NULL;
91 m_nFirstBlockPtr = 0;
92 m_nBlockSize = m_nSizeUsed = m_nFileOffset = m_nCurPos = 0;
93 m_bHardBlockSize = bHardBlockSize;
94 m_nFileSize = -1;
95
96 m_bModified = FALSE;
97
98 m_eAccess = eAccessMode;
99
100 }
101
102 /**********************************************************************
103 * TABRawBinBlock::~TABRawBinBlock()
104 *
105 * Destructor.
106 **********************************************************************/
~TABRawBinBlock()107 TABRawBinBlock::~TABRawBinBlock()
108 {
109 if (m_pabyBuf)
110 CPLFree(m_pabyBuf);
111 }
112
113
114 /**********************************************************************
115 * TABRawBinBlock::ReadFromFile()
116 *
117 * Load data from the specified file location and initialize the block.
118 *
119 * Returns 0 if succesful or -1 if an error happened, in which case
120 * CPLError() will have been called.
121 **********************************************************************/
ReadFromFile(VSILFILE * fpSrc,int nOffset,int nSize)122 int TABRawBinBlock::ReadFromFile(VSILFILE *fpSrc, int nOffset,
123 int nSize /*= 512*/)
124 {
125 GByte *pabyBuf;
126
127 if (fpSrc == NULL || nSize == 0)
128 {
129 CPLError(CE_Failure, CPLE_AssertionFailed,
130 "TABRawBinBlock::ReadFromFile(): Assertion Failed!");
131 return -1;
132 }
133
134 m_fp = fpSrc;
135
136 VSIFSeekL(fpSrc, 0, SEEK_END);
137 m_nFileSize = (int)VSIFTellL(m_fp);
138
139 m_nFileOffset = nOffset;
140 m_nCurPos = 0;
141 m_bModified = FALSE;
142
143 /*----------------------------------------------------------------
144 * Alloc a buffer to contain the data
145 *---------------------------------------------------------------*/
146 pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte));
147
148 /*----------------------------------------------------------------
149 * Read from the file
150 *---------------------------------------------------------------*/
151 if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
152 (m_nSizeUsed = VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc) ) == 0 ||
153 (m_bHardBlockSize && m_nSizeUsed != nSize ) )
154 {
155 CPLError(CE_Failure, CPLE_FileIO,
156 "ReadFromFile() failed reading %d bytes at offset %d.",
157 nSize, nOffset);
158 CPLFree(pabyBuf);
159 return -1;
160 }
161
162 /*----------------------------------------------------------------
163 * Init block with the data we just read
164 *---------------------------------------------------------------*/
165 return InitBlockFromData(pabyBuf, nSize, m_nSizeUsed,
166 FALSE, fpSrc, nOffset);
167 }
168
169
170 /**********************************************************************
171 * TABRawBinBlock::CommitToFile()
172 *
173 * Commit the current state of the binary block to the file to which
174 * it has been previously attached.
175 *
176 * Derived classes may want to (optionally) reimplement this method if
177 * they need to do special processing before committing the block to disk.
178 *
179 * For files created with bHardBlockSize=TRUE, a complete block of
180 * the specified size is always written, otherwise only the number of
181 * used bytes in the block will be written to disk.
182 *
183 * Returns 0 if succesful or -1 if an error happened, in which case
184 * CPLError() will have been called.
185 **********************************************************************/
CommitToFile()186 int TABRawBinBlock::CommitToFile()
187 {
188 int nStatus = 0;
189
190 if (m_fp == NULL || m_nBlockSize <= 0 || m_pabyBuf == NULL ||
191 m_nFileOffset < 0)
192 {
193 CPLError(CE_Failure, CPLE_AssertionFailed,
194 "TABRawBinBlock::CommitToFile(): Block has not been initialized yet!");
195 return -1;
196 }
197
198 /*----------------------------------------------------------------
199 * If block has not been modified, then just return... nothing to do.
200 *---------------------------------------------------------------*/
201 if (!m_bModified)
202 return 0;
203
204 /*----------------------------------------------------------------
205 * Move the output file pointer to the right position...
206 *---------------------------------------------------------------*/
207 if (VSIFSeekL(m_fp, m_nFileOffset, SEEK_SET) != 0)
208 {
209 /*------------------------------------------------------------
210 * Moving pointer failed... we may need to pad with zeros if
211 * block destination is beyond current end of file.
212 *-----------------------------------------------------------*/
213 int nCurPos;
214 nCurPos = (int)VSIFTellL(m_fp);
215
216 if (nCurPos < m_nFileOffset &&
217 VSIFSeekL(m_fp, 0L, SEEK_END) == 0 &&
218 (nCurPos = (int)VSIFTellL(m_fp)) < m_nFileOffset)
219 {
220 GByte cZero = 0;
221
222 while(nCurPos < m_nFileOffset && nStatus == 0)
223 {
224 if (VSIFWriteL(&cZero, 1, 1, m_fp) != 1)
225 {
226 CPLError(CE_Failure, CPLE_FileIO,
227 "Failed writing 1 byte at offset %d.", nCurPos);
228 nStatus = -1;
229 break;
230 }
231 nCurPos++;
232 }
233 }
234
235 if (nCurPos != m_nFileOffset)
236 nStatus = -1; // Error message will follow below
237
238 }
239
240 /*----------------------------------------------------------------
241 * At this point we are ready to write to the file.
242 *
243 * If m_bHardBlockSize==FALSE, then we do not write a complete block;
244 * we write only the part of the block that was used.
245 *---------------------------------------------------------------*/
246 int numBytesToWrite = m_bHardBlockSize?m_nBlockSize:m_nSizeUsed;
247
248 /*CPLDebug("MITAB", "Commiting to offset %d", m_nFileOffset);*/
249
250 if (nStatus != 0 ||
251 VSIFWriteL(m_pabyBuf,sizeof(GByte),
252 numBytesToWrite, m_fp) != (size_t)numBytesToWrite )
253 {
254 CPLError(CE_Failure, CPLE_FileIO,
255 "Failed writing %d bytes at offset %d.",
256 numBytesToWrite, m_nFileOffset);
257 return -1;
258 }
259 if( m_nFileOffset + numBytesToWrite > m_nFileSize )
260 {
261 m_nFileSize = m_nFileOffset + numBytesToWrite;
262 }
263
264 VSIFFlushL(m_fp);
265
266 m_bModified = FALSE;
267
268 return 0;
269 }
270
271 /**********************************************************************
272 * TABRawBinBlock::CommitAsDeleted()
273 *
274 * Commit current block to file using block type 4 (garbage block)
275 *
276 * Returns 0 if succesful or -1 if an error happened, in which case
277 * CPLError() will have been called.
278 **********************************************************************/
CommitAsDeleted(GInt32 nNextBlockPtr)279 int TABRawBinBlock::CommitAsDeleted(GInt32 nNextBlockPtr)
280 {
281 int nStatus = 0;
282
283 CPLErrorReset();
284
285 if ( m_pabyBuf == NULL )
286 {
287 CPLError(CE_Failure, CPLE_AssertionFailed,
288 "CommitAsDeleted(): Block has not been initialized yet!");
289 return -1;
290 }
291
292 /*-----------------------------------------------------------------
293 * Create deleted block header
294 *----------------------------------------------------------------*/
295 GotoByteInBlock(0x000);
296 WriteInt16(TABMAP_GARB_BLOCK); // Block type code
297 WriteInt32(nNextBlockPtr);
298
299 if( CPLGetLastErrorType() == CE_Failure )
300 nStatus = CPLGetLastErrorNo();
301
302 /*-----------------------------------------------------------------
303 * OK, call the base class to write the block to disk.
304 *----------------------------------------------------------------*/
305 if (nStatus == 0)
306 {
307 #ifdef DEBUG_VERBOSE
308 CPLDebug("MITAB", "Commiting GARBAGE block to offset %d", m_nFileOffset);
309 #endif
310 nStatus = TABRawBinBlock::CommitToFile();
311 m_nSizeUsed = 0;
312 }
313
314 return nStatus;
315 }
316
317 /**********************************************************************
318 * TABRawBinBlock::InitBlockFromData()
319 *
320 * Set the binary data buffer and initialize the block.
321 *
322 * Calling ReadFromFile() will automatically call InitBlockFromData() to
323 * complete the initialization of the block after the data is read from the
324 * file. Derived classes should implement their own version of
325 * InitBlockFromData() if they need specific initialization... in this
326 * case the derived InitBlockFromData() should call
327 * TABRawBinBlock::InitBlockFromData() before doing anything else.
328 *
329 * By default, the buffer will be copied, but if bMakeCopy = FALSE then
330 * it won't be copied, and the object will keep a reference to the
331 * user's buffer... and this object will eventually free the user's buffer.
332 *
333 * Returns 0 if succesful or -1 if an error happened, in which case
334 * CPLError() will have been called.
335 **********************************************************************/
InitBlockFromData(GByte * pabyBuf,int nBlockSize,int nSizeUsed,GBool bMakeCopy,VSILFILE * fpSrc,int nOffset)336 int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf,
337 int nBlockSize, int nSizeUsed,
338 GBool bMakeCopy /* = TRUE */,
339 VSILFILE *fpSrc /* = NULL */,
340 int nOffset /* = 0 */)
341 {
342 m_fp = fpSrc;
343 m_nFileOffset = nOffset;
344 m_nCurPos = 0;
345 m_bModified = FALSE;
346
347 /*----------------------------------------------------------------
348 * Alloc or realloc the buffer to contain the data if necessary
349 *---------------------------------------------------------------*/
350 if (!bMakeCopy)
351 {
352 if (m_pabyBuf != NULL)
353 CPLFree(m_pabyBuf);
354 m_pabyBuf = pabyBuf;
355 m_nBlockSize = nBlockSize;
356 m_nSizeUsed = nSizeUsed;
357 }
358 else if (m_pabyBuf == NULL || nBlockSize != m_nBlockSize)
359 {
360 m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, nBlockSize*sizeof(GByte));
361 m_nBlockSize = nBlockSize;
362 m_nSizeUsed = nSizeUsed;
363 memcpy(m_pabyBuf, pabyBuf, m_nSizeUsed);
364 }
365
366 /*----------------------------------------------------------------
367 * Extract block type... header block (first block in a file) has
368 * no block type, so we assign one by default.
369 *---------------------------------------------------------------*/
370 if (m_nFileOffset == 0)
371 m_nBlockType = TABMAP_HEADER_BLOCK;
372 else
373 {
374 // Block type will be validated only if GetBlockType() is called
375 m_nBlockType = (int)m_pabyBuf[0];
376 }
377
378 return 0;
379 }
380
381 /**********************************************************************
382 * TABRawBinBlock::InitNewBlock()
383 *
384 * Initialize the block so that it knows to which file is is attached,
385 * its block size, etc.
386 *
387 * This is an alternative to calling ReadFromFile() or InitBlockFromData()
388 * that puts the block in a stable state without loading any initial
389 * data in it.
390 *
391 * Returns 0 if succesful or -1 if an error happened, in which case
392 * CPLError() will have been called.
393 **********************************************************************/
InitNewBlock(VSILFILE * fpSrc,int nBlockSize,int nFileOffset)394 int TABRawBinBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
395 int nFileOffset /* = 0*/)
396 {
397 m_fp = fpSrc;
398 m_nBlockSize = nBlockSize;
399 m_nSizeUsed = 0;
400 m_nCurPos = 0;
401 m_bModified = FALSE;
402
403 if (nFileOffset > 0)
404 m_nFileOffset = nFileOffset;
405 else
406 m_nFileOffset = 0;
407
408 if( m_fp != NULL && m_nFileSize < 0 && m_eAccess == TABReadWrite )
409 {
410 int nCurPos = (int)VSIFTellL(m_fp);
411 VSIFSeekL(fpSrc, 0, SEEK_END);
412 m_nFileSize = (int)VSIFTellL(m_fp);
413 VSIFSeekL(fpSrc, nCurPos, SEEK_SET);
414 }
415
416 m_nBlockType = -1;
417
418 m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, m_nBlockSize*sizeof(GByte));
419 memset(m_pabyBuf, 0, m_nBlockSize);
420
421 return 0;
422 }
423
424
425 /**********************************************************************
426 * TABRawBinBlock::GetBlockType()
427 *
428 * Return the block type for the current object.
429 *
430 * Returns a block type >= 0 if succesful or -1 if an error happened, in
431 * which case CPLError() will have been called.
432 **********************************************************************/
GetBlockType()433 int TABRawBinBlock::GetBlockType()
434 {
435 if (m_pabyBuf == NULL)
436 {
437 CPLError(CE_Failure, CPLE_AppDefined,
438 "GetBlockType(): Block has not been initialized.");
439 return -1;
440 }
441
442 if (m_nBlockType > TABMAP_LAST_VALID_BLOCK_TYPE)
443 {
444 CPLError(CE_Failure, CPLE_NotSupported,
445 "GetBlockType(): Unsupported block type %d.",
446 m_nBlockType);
447 return -1;
448 }
449
450 return m_nBlockType;
451 }
452
453 /**********************************************************************
454 * TABRawBinBlock::GotoByteInBlock()
455 *
456 * Move the block pointer to the specified position relative to the
457 * beginning of the block.
458 *
459 * Returns 0 if succesful or -1 if an error happened, in which case
460 * CPLError() will have been called.
461 **********************************************************************/
GotoByteInBlock(int nOffset)462 int TABRawBinBlock::GotoByteInBlock(int nOffset)
463 {
464 if ( (m_eAccess == TABRead && nOffset > m_nSizeUsed) ||
465 (m_eAccess != TABRead && nOffset > m_nBlockSize) )
466 {
467 CPLError(CE_Failure, CPLE_AppDefined,
468 "GotoByteInBlock(): Attempt to go past end of data block.");
469 return -1;
470 }
471
472 if (nOffset < 0)
473 {
474 CPLError(CE_Failure, CPLE_AppDefined,
475 "GotoByteInBlock(): Attempt to go before start of data block.");
476 return -1;
477 }
478
479 m_nCurPos = nOffset;
480
481 m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
482
483 return 0;
484 }
485
486 /**********************************************************************
487 * TABRawBinBlock::GotoByteRel()
488 *
489 * Move the block pointer by the specified number of bytes relative
490 * to its current position.
491 *
492 * Returns 0 if succesful or -1 if an error happened, in which case
493 * CPLError() will have been called.
494 **********************************************************************/
GotoByteRel(int nOffset)495 int TABRawBinBlock::GotoByteRel(int nOffset)
496 {
497 return GotoByteInBlock(m_nCurPos + nOffset);
498 }
499
500 /**********************************************************************
501 * TABRawBinBlock::GotoByteInFile()
502 *
503 * Move the block pointer to the specified position relative to the
504 * beginning of the file.
505 *
506 * In read access, the current block may be reloaded to contain a right
507 * block of binary data if necessary.
508 *
509 * In write mode, the current block may automagically be committed to
510 * disk and a new block initialized if necessary.
511 *
512 * bForceReadFromFile is used in write mode to read the new block data from
513 * file instead of creating an empty block. (Useful for TABCollection
514 * or other cases that need to do random access in the file in write mode.)
515 *
516 * bOffsetIsEndOfData is set to TRUE to indicate that the nOffset
517 * to which we are attempting to go is the end of the used data in this
518 * block (we are positioninig ourselves to append data), so if the nOffset
519 * corresponds to the beginning of a 512 bytes block then we should really
520 * be positioning ourselves at the end of the block that ends at this
521 * address instead of at the beginning of the blocks that starts at this
522 * address. This case can happen when going back and forth to write collection
523 * objects to a Coordblock and is documented in bug 1657.
524 *
525 * Returns 0 if succesful or -1 if an error happened, in which case
526 * CPLError() will have been called.
527 **********************************************************************/
GotoByteInFile(int nOffset,GBool bForceReadFromFile,GBool bOffsetIsEndOfData)528 int TABRawBinBlock::GotoByteInFile(int nOffset,
529 GBool bForceReadFromFile /*=FALSE*/,
530 GBool bOffsetIsEndOfData /*=FALSE*/)
531 {
532 int nNewBlockPtr;
533
534 if (nOffset < 0)
535 {
536 CPLError(CE_Failure, CPLE_AppDefined,
537 "GotoByteInFile(): Attempt to go before start of file.");
538 return -1;
539 }
540
541 nNewBlockPtr = ( (nOffset-m_nFirstBlockPtr)/m_nBlockSize)*m_nBlockSize +
542 m_nFirstBlockPtr;
543
544 if (m_eAccess == TABRead)
545 {
546 if ( (nOffset<m_nFileOffset || nOffset>=m_nFileOffset+m_nSizeUsed) &&
547 ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)
548 {
549 // Failed reading new block... error has already been reported.
550 return -1;
551 }
552 }
553 else if (m_eAccess == TABWrite)
554 {
555 if ( (nOffset<m_nFileOffset || nOffset>=m_nFileOffset+m_nBlockSize) &&
556 (CommitToFile() != 0 ||
557 InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0 ) )
558 {
559 // Failed reading new block... error has already been reported.
560 return -1;
561 }
562 }
563 else if (m_eAccess == TABReadWrite)
564 {
565 // TODO: THIS IS NOT REAL read/write access (it's more extended write)
566 // Currently we try to read from file only if explicitly requested.
567 // If we ever want true read/write mode we should implement
568 // more smarts to detect whether the caller wants an existing block to
569 // be read, or a new one to be created from scratch.
570 // CommitToFile() should only be called only if something changed.
571 //
572 if (bOffsetIsEndOfData && nOffset%m_nBlockSize == 0)
573 {
574 /* We're trying to go byte 512 of a block that's full of data.
575 * In this case it's okay to place the m_nCurPos at byte 512
576 * which is past the end of the block.
577 */
578
579 /* Make sure we request the block that ends with requested
580 * address and not the following block that doesn't exist
581 * yet on disk */
582 nNewBlockPtr -= m_nBlockSize;
583
584 if ( (nOffset < m_nFileOffset ||
585 nOffset > m_nFileOffset+m_nBlockSize) &&
586 (CommitToFile() != 0 ||
587 (!bForceReadFromFile &&
588 InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
589 (bForceReadFromFile &&
590 ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) ) )
591 {
592 // Failed reading new block... error has already been reported.
593 return -1;
594 }
595 }
596 else
597 {
598 if( !bForceReadFromFile && m_nFileSize > 0 &&
599 nOffset < m_nFileSize )
600 {
601 bForceReadFromFile = TRUE;
602 if ( !(nOffset < m_nFileOffset ||
603 nOffset >= m_nFileOffset+m_nBlockSize) )
604 {
605 if ( (nOffset<m_nFileOffset || nOffset>=m_nFileOffset+m_nSizeUsed) &&
606 (CommitToFile() != 0 ||
607 ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) )
608 {
609 // Failed reading new block... error has already been reported.
610 return -1;
611 }
612 }
613 }
614
615 if ( (nOffset < m_nFileOffset ||
616 nOffset >= m_nFileOffset+m_nBlockSize) &&
617 (CommitToFile() != 0 ||
618 (!bForceReadFromFile &&
619 InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
620 (bForceReadFromFile &&
621 ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) ) )
622 {
623 // Failed reading new block... error has already been reported.
624 return -1;
625 }
626 }
627 }
628 else
629 {
630 CPLError(CE_Failure, CPLE_NotSupported,
631 "Access mode not supported yet!");
632 return -1;
633 }
634
635 m_nCurPos = nOffset-m_nFileOffset;
636
637 m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
638
639 return 0;
640 }
641
642
643 /**********************************************************************
644 * TABRawBinBlock::SetFirstBlockPtr()
645 *
646 * Set the position in the file at which the first block starts.
647 * This value will usually be the header size and needs to be specified
648 * only if the header size is different from the other blocks size.
649 *
650 * This value will be used by GotoByteInFile() to properly align the data
651 * blocks that it loads automatically when a requested position is outside
652 * of the block currently in memory.
653 **********************************************************************/
SetFirstBlockPtr(int nOffset)654 void TABRawBinBlock::SetFirstBlockPtr(int nOffset)
655 {
656 m_nFirstBlockPtr = nOffset;
657 }
658
659
660 /**********************************************************************
661 * TABRawBinBlock::GetNumUnusedBytes()
662 *
663 * Return the number of unused bytes in this block.
664 **********************************************************************/
GetNumUnusedBytes()665 int TABRawBinBlock::GetNumUnusedBytes()
666 {
667 return (m_nBlockSize - m_nSizeUsed);
668 }
669
670 /**********************************************************************
671 * TABRawBinBlock::GetFirstUnusedByteOffset()
672 *
673 * Return the position of the first unused byte in this block relative
674 * to the beginning of the file, or -1 if the block is full.
675 **********************************************************************/
GetFirstUnusedByteOffset()676 int TABRawBinBlock::GetFirstUnusedByteOffset()
677 {
678 if (m_nSizeUsed < m_nBlockSize)
679 return m_nFileOffset + m_nSizeUsed;
680 else
681 return -1;
682 }
683
684 /**********************************************************************
685 * TABRawBinBlock::GetCurAddress()
686 *
687 * Return the current pointer position, relative to beginning of file.
688 **********************************************************************/
GetCurAddress()689 int TABRawBinBlock::GetCurAddress()
690 {
691 return (m_nFileOffset + m_nCurPos);
692 }
693
694 /**********************************************************************
695 * TABRawBinBlock::ReadBytes()
696 *
697 * Copy the number of bytes from the data block's internal buffer to
698 * the user's buffer pointed by pabyDstBuf.
699 *
700 * Passing pabyDstBuf = NULL will only move the read pointer by the
701 * specified number of bytes as if the copy had happened... but it
702 * won't crash.
703 *
704 * Returns 0 if succesful or -1 if an error happened, in which case
705 * CPLError() will have been called.
706 **********************************************************************/
ReadBytes(int numBytes,GByte * pabyDstBuf)707 int TABRawBinBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
708 {
709 /*----------------------------------------------------------------
710 * Make sure block is initialized with Read access and that the
711 * operation won't go beyond the buffer's size.
712 *---------------------------------------------------------------*/
713 if (m_pabyBuf == NULL)
714 {
715 CPLError(CE_Failure, CPLE_AppDefined,
716 "ReadBytes(): Block has not been initialized.");
717 return -1;
718 }
719
720 if (m_nCurPos + numBytes > m_nSizeUsed)
721 {
722 CPLError(CE_Failure, CPLE_AppDefined,
723 "ReadBytes(): Attempt to read past end of data block.");
724 return -1;
725 }
726
727 if (pabyDstBuf)
728 {
729 memcpy(pabyDstBuf, m_pabyBuf + m_nCurPos, numBytes);
730 }
731
732 m_nCurPos += numBytes;
733
734 return 0;
735 }
736
737 /**********************************************************************
738 * TABRawBinBlock::Read<datatype>()
739 *
740 * MapInfo files are binary files with LSB first (Intel) byte
741 * ordering. The following functions will read from the input file
742 * and return a value with the bytes ordered properly for the current
743 * platform.
744 **********************************************************************/
ReadByte()745 GByte TABRawBinBlock::ReadByte()
746 {
747 GByte byValue;
748
749 ReadBytes(1, (GByte*)(&byValue));
750
751 return byValue;
752 }
753
ReadInt16()754 GInt16 TABRawBinBlock::ReadInt16()
755 {
756 GInt16 n16Value;
757
758 ReadBytes(2, (GByte*)(&n16Value));
759
760 #ifdef CPL_MSB
761 return (GInt16)CPL_SWAP16(n16Value);
762 #else
763 return n16Value;
764 #endif
765 }
766
ReadInt32()767 GInt32 TABRawBinBlock::ReadInt32()
768 {
769 GInt32 n32Value;
770
771 ReadBytes(4, (GByte*)(&n32Value));
772
773 #ifdef CPL_MSB
774 return (GInt32)CPL_SWAP32(n32Value);
775 #else
776 return n32Value;
777 #endif
778 }
779
ReadFloat()780 float TABRawBinBlock::ReadFloat()
781 {
782 float fValue;
783
784 ReadBytes(4, (GByte*)(&fValue));
785
786 #ifdef CPL_MSB
787 *(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue));
788 #endif
789 return fValue;
790 }
791
ReadDouble()792 double TABRawBinBlock::ReadDouble()
793 {
794 double dValue;
795
796 ReadBytes(8, (GByte*)(&dValue));
797
798 #ifdef CPL_MSB
799 CPL_SWAPDOUBLE(&dValue);
800 #endif
801
802 return dValue;
803 }
804
805
806
807 /**********************************************************************
808 * TABRawBinBlock::WriteBytes()
809 *
810 * Copy the number of bytes from the user's buffer pointed by pabySrcBuf
811 * to the data block's internal buffer.
812 * Note that this call only writes to the memory buffer... nothing is
813 * written to the file until WriteToFile() is called.
814 *
815 * Passing pabySrcBuf = NULL will only move the write pointer by the
816 * specified number of bytes as if the copy had happened... but it
817 * won't crash.
818 *
819 * Returns 0 if succesful or -1 if an error happened, in which case
820 * CPLError() will have been called.
821 **********************************************************************/
WriteBytes(int nBytesToWrite,GByte * pabySrcBuf)822 int TABRawBinBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf)
823 {
824 /*----------------------------------------------------------------
825 * Make sure block is initialized with Write access and that the
826 * operation won't go beyond the buffer's size.
827 *---------------------------------------------------------------*/
828 if (m_pabyBuf == NULL)
829 {
830 CPLError(CE_Failure, CPLE_AppDefined,
831 "WriteBytes(): Block has not been initialized.");
832 return -1;
833 }
834
835 if (m_eAccess == TABRead )
836 {
837 CPLError(CE_Failure, CPLE_AppDefined,
838 "WriteBytes(): Block does not support write operations.");
839 return -1;
840 }
841
842 if (m_nCurPos + nBytesToWrite > m_nBlockSize)
843 {
844 CPLError(CE_Failure, CPLE_AppDefined,
845 "WriteBytes(): Attempt to write past end of data block.");
846 return -1;
847 }
848
849 /*----------------------------------------------------------------
850 * Everything is OK... copy the data
851 *---------------------------------------------------------------*/
852 if (pabySrcBuf)
853 {
854 memcpy(m_pabyBuf + m_nCurPos, pabySrcBuf, nBytesToWrite);
855 }
856
857 m_nCurPos += nBytesToWrite;
858
859 m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
860
861 m_bModified = TRUE;
862
863 return 0;
864 }
865
866
867 /**********************************************************************
868 * TABRawBinBlock::Write<datatype>()
869 *
870 * Arc/Info files are binary files with MSB first (Motorola) byte
871 * ordering. The following functions will reorder the byte for the
872 * value properly and write that to the output file.
873 *
874 * If a problem happens, then CPLError() will be called and
875 * CPLGetLastErrNo() can be used to test if a write operation was
876 * succesful.
877 **********************************************************************/
WriteByte(GByte byValue)878 int TABRawBinBlock::WriteByte(GByte byValue)
879 {
880 return WriteBytes(1, (GByte*)&byValue);
881 }
882
WriteInt16(GInt16 n16Value)883 int TABRawBinBlock::WriteInt16(GInt16 n16Value)
884 {
885 #ifdef CPL_MSB
886 n16Value = (GInt16)CPL_SWAP16(n16Value);
887 #endif
888
889 return WriteBytes(2, (GByte*)&n16Value);
890 }
891
WriteInt32(GInt32 n32Value)892 int TABRawBinBlock::WriteInt32(GInt32 n32Value)
893 {
894 #ifdef CPL_MSB
895 n32Value = (GInt32)CPL_SWAP32(n32Value);
896 #endif
897
898 return WriteBytes(4, (GByte*)&n32Value);
899 }
900
WriteFloat(float fValue)901 int TABRawBinBlock::WriteFloat(float fValue)
902 {
903 #ifdef CPL_MSB
904 *(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue));
905 #endif
906
907 return WriteBytes(4, (GByte*)&fValue);
908 }
909
WriteDouble(double dValue)910 int TABRawBinBlock::WriteDouble(double dValue)
911 {
912 #ifdef CPL_MSB
913 CPL_SWAPDOUBLE(&dValue);
914 #endif
915
916 return WriteBytes(8, (GByte*)&dValue);
917 }
918
919
920 /**********************************************************************
921 * TABRawBinBlock::WriteZeros()
922 *
923 * Write a number of zeros (sepcified in bytes) at the current position
924 * in the file.
925 *
926 * If a problem happens, then CPLError() will be called and
927 * CPLGetLastErrNo() can be used to test if a write operation was
928 * succesful.
929 **********************************************************************/
WriteZeros(int nBytesToWrite)930 int TABRawBinBlock::WriteZeros(int nBytesToWrite)
931 {
932 char acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
933 int i;
934 int nStatus = 0;
935
936 /* Write by 8 bytes chunks. The last chunk may be less than 8 bytes
937 */
938 for(i=0; nStatus == 0 && i< nBytesToWrite; i+=8)
939 {
940 nStatus = WriteBytes(MIN(8,(nBytesToWrite-i)), (GByte*)acZeros);
941 }
942
943 return nStatus;
944 }
945
946 /**********************************************************************
947 * TABRawBinBlock::WritePaddedString()
948 *
949 * Write a string and pad the end of the field (up to nFieldSize) with
950 * spaces number of spaces at the current position in the file.
951 *
952 * If a problem happens, then CPLError() will be called and
953 * CPLGetLastErrNo() can be used to test if a write operation was
954 * succesful.
955 **********************************************************************/
WritePaddedString(int nFieldSize,const char * pszString)956 int TABRawBinBlock::WritePaddedString(int nFieldSize, const char *pszString)
957 {
958 char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
959 int i, nLen, numSpaces;
960 int nStatus = 0;
961
962 nLen = strlen(pszString);
963 nLen = MIN(nLen, nFieldSize);
964 numSpaces = nFieldSize - nLen;
965
966 if (nLen > 0)
967 nStatus = WriteBytes(nLen, (GByte*)pszString);
968
969 /* Write spaces by 8 bytes chunks. The last chunk may be less than 8 bytes
970 */
971 for(i=0; nStatus == 0 && i< numSpaces; i+=8)
972 {
973 nStatus = WriteBytes(MIN(8,(numSpaces-i)), (GByte*)acSpaces);
974 }
975
976 return nStatus;
977 }
978
979 /**********************************************************************
980 * TABRawBinBlock::Dump()
981 *
982 * Dump block contents... available only in DEBUG mode.
983 **********************************************************************/
984 #ifdef DEBUG
985
Dump(FILE * fpOut)986 void TABRawBinBlock::Dump(FILE *fpOut /*=NULL*/)
987 {
988 if (fpOut == NULL)
989 fpOut = stdout;
990
991 fprintf(fpOut, "----- TABRawBinBlock::Dump() -----\n");
992 if (m_pabyBuf == NULL)
993 {
994 fprintf(fpOut, "Block has not been initialized yet.");
995 }
996 else
997 {
998 if( m_nBlockType == TABMAP_GARB_BLOCK )
999 {
1000 fprintf(fpOut,"Garbage Block (type %d) at offset %d.\n",
1001 m_nBlockType, m_nFileOffset);
1002 int nNextGarbageBlock;
1003 memcpy(&nNextGarbageBlock, m_pabyBuf + 2, 4);
1004 CPL_LSBPTR32(&nNextGarbageBlock);
1005 fprintf(fpOut," m_nNextGarbageBlock = %d\n", nNextGarbageBlock);
1006 }
1007 else
1008 {
1009 fprintf(fpOut, "Block (type %d) size=%d bytes at offset %d in file.\n",
1010 m_nBlockType, m_nBlockSize, m_nFileOffset);
1011 fprintf(fpOut, "Current pointer at byte %d\n", m_nCurPos);
1012 }
1013 }
1014
1015 fflush(fpOut);
1016 }
1017
1018 #endif // DEBUG
1019
1020
1021 /**********************************************************************
1022 * DumpBytes()
1023 *
1024 * Read and dump the contents of an Binary file.
1025 **********************************************************************/
DumpBytes(GInt32 nValue,int nOffset,FILE * fpOut)1026 void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/,
1027 FILE *fpOut /*=NULL*/)
1028 {
1029 GInt32 anVal[2];
1030 GInt16 n16Val1, n16Val2;
1031 float fValue;
1032 char *pcValue;
1033 double dValue;
1034
1035 pcValue = (char*)&nValue;
1036 memcpy(&fValue, &nValue, 4);
1037
1038 memcpy(&n16Val1, pcValue + 2, sizeof(GInt16));
1039 memcpy(&n16Val2, pcValue, sizeof(GInt16));
1040
1041 anVal[0] = anVal[1] = 0;
1042
1043 /* For double precision values, we only use the first half
1044 * of the height bytes... and leave the other 4 bytes as zeros!
1045 * It's a bit of a hack, but it seems to be enough for the
1046 * precision of the values we print!
1047 */
1048 #ifdef CPL_MSB
1049 anVal[0] = nValue;
1050 #else
1051 anVal[1] = nValue;
1052 #endif
1053 memcpy(&dValue, anVal, 8);
1054
1055 if (fpOut == NULL)
1056 fpOut = stdout;
1057
1058 fprintf(fpOut, "%d\t0x%8.8x %-5d\t%-6d %-6d %5.3e d=%5.3e",
1059 nOffset, nValue, nValue,
1060 n16Val1, n16Val2, fValue, dValue);
1061
1062 printf("\t[%c%c%c%c]\n", isprint(pcValue[0])?pcValue[0]:'.',
1063 isprint(pcValue[1])?pcValue[1]:'.',
1064 isprint(pcValue[2])?pcValue[2]:'.',
1065 isprint(pcValue[3])?pcValue[3]:'.');
1066 }
1067
1068
1069
1070 /**********************************************************************
1071 * TABCreateMAPBlockFromFile()
1072 *
1073 * Load data from the specified file location and create and initialize
1074 * a TABMAP*Block of the right type to handle it.
1075 *
1076 * Returns the new object if succesful or NULL if an error happened, in
1077 * which case CPLError() will have been called.
1078 **********************************************************************/
TABCreateMAPBlockFromFile(VSILFILE * fpSrc,int nOffset,int nSize,GBool bHardBlockSize,TABAccess eAccessMode)1079 TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset,
1080 int nSize /*= 512*/,
1081 GBool bHardBlockSize /*= TRUE */,
1082 TABAccess eAccessMode /*= TABRead*/)
1083 {
1084 TABRawBinBlock *poBlock = NULL;
1085 GByte *pabyBuf;
1086
1087 if (fpSrc == NULL || nSize == 0)
1088 {
1089 CPLError(CE_Failure, CPLE_AssertionFailed,
1090 "TABCreateMAPBlockFromFile(): Assertion Failed!");
1091 return NULL;
1092 }
1093
1094 /*----------------------------------------------------------------
1095 * Alloc a buffer to contain the data
1096 *---------------------------------------------------------------*/
1097 pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte));
1098
1099 /*----------------------------------------------------------------
1100 * Read from the file
1101 *---------------------------------------------------------------*/
1102 if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
1103 VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc)!=(unsigned int)nSize )
1104 {
1105 CPLError(CE_Failure, CPLE_FileIO,
1106 "TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.",
1107 nSize, nOffset);
1108 CPLFree(pabyBuf);
1109 return NULL;
1110 }
1111
1112 /*----------------------------------------------------------------
1113 * Create an object of the right type
1114 * Header block is different: it does not start with the object
1115 * type byte but it is always the first block in a file
1116 *---------------------------------------------------------------*/
1117 if (nOffset == 0)
1118 {
1119 poBlock = new TABMAPHeaderBlock(eAccessMode);
1120 }
1121 else
1122 {
1123 switch(pabyBuf[0])
1124 {
1125 case TABMAP_INDEX_BLOCK:
1126 poBlock = new TABMAPIndexBlock(eAccessMode);
1127 break;
1128 case TABMAP_OBJECT_BLOCK:
1129 poBlock = new TABMAPObjectBlock(eAccessMode);
1130 break;
1131 case TABMAP_COORD_BLOCK:
1132 poBlock = new TABMAPCoordBlock(eAccessMode);
1133 break;
1134 case TABMAP_TOOL_BLOCK:
1135 poBlock = new TABMAPToolBlock(eAccessMode);
1136 break;
1137 case TABMAP_GARB_BLOCK:
1138 default:
1139 poBlock = new TABRawBinBlock(eAccessMode, bHardBlockSize);
1140 break;
1141 }
1142 }
1143
1144 /*----------------------------------------------------------------
1145 * Init new object with the data we just read
1146 *---------------------------------------------------------------*/
1147 if (poBlock->InitBlockFromData(pabyBuf, nSize, nSize,
1148 FALSE, fpSrc, nOffset) != 0)
1149 {
1150 // Some error happened... and CPLError() has been called
1151 delete poBlock;
1152 poBlock = NULL;
1153 }
1154
1155 return poBlock;
1156 }
1157
1158 /*=====================================================================
1159 * class TABBinBlockManager
1160 *====================================================================*/
1161
1162
1163 /**********************************************************************
1164 * TABBinBlockManager::TABBinBlockManager()
1165 *
1166 * Constructor.
1167 **********************************************************************/
TABBinBlockManager(int nBlockSize)1168 TABBinBlockManager::TABBinBlockManager(int nBlockSize /*=512*/)
1169 {
1170
1171 m_nBlockSize=nBlockSize;
1172 m_nLastAllocatedBlock = -1;
1173 m_psGarbageBlocksFirst = NULL;
1174 m_psGarbageBlocksLast = NULL;
1175 m_szName[0] = '\0';
1176 }
1177
1178 /**********************************************************************
1179 * TABBinBlockManager::~TABBinBlockManager()
1180 *
1181 * Destructor.
1182 **********************************************************************/
~TABBinBlockManager()1183 TABBinBlockManager::~TABBinBlockManager()
1184 {
1185 Reset();
1186 }
1187
1188 /**********************************************************************
1189 * TABBinBlockManager::SetName()
1190 **********************************************************************/
SetName(const char * pszName)1191 void TABBinBlockManager::SetName(const char* pszName)
1192 {
1193 strncpy(m_szName, pszName, sizeof(m_szName));
1194 m_szName[sizeof(m_szName)-1] = '\0';
1195 }
1196
1197 /**********************************************************************
1198 * TABBinBlockManager::AllocNewBlock()
1199 *
1200 * Returns and reserves the address of the next available block, either a
1201 * brand new block at end of file, or recycle a garbage block if one is
1202 * available.
1203 **********************************************************************/
AllocNewBlock(CPL_UNUSED const char * pszReason)1204 GInt32 TABBinBlockManager::AllocNewBlock(CPL_UNUSED const char* pszReason)
1205 {
1206 // Try to reuse garbage blocks first
1207 if (GetFirstGarbageBlock() > 0)
1208 {
1209 int nRetValue = PopGarbageBlock();
1210 #ifdef DEBUG_VERBOSE
1211 CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d (recycling garbage block)", m_szName, pszReason, nRetValue);
1212 #endif
1213 return nRetValue;
1214 }
1215
1216 // ... or alloc a new block at EOF
1217 if (m_nLastAllocatedBlock==-1)
1218 m_nLastAllocatedBlock = 0;
1219 else
1220 m_nLastAllocatedBlock+=m_nBlockSize;
1221
1222 #ifdef DEBUG_VERBOSE
1223 CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d", m_szName, pszReason, m_nLastAllocatedBlock);
1224 #endif
1225 return m_nLastAllocatedBlock;
1226 }
1227
1228 /**********************************************************************
1229 * TABBinBlockManager::Reset()
1230 *
1231 **********************************************************************/
Reset()1232 void TABBinBlockManager::Reset()
1233 {
1234 m_nLastAllocatedBlock = -1;
1235
1236 // Flush list of garbage blocks
1237 while (m_psGarbageBlocksFirst != NULL)
1238 {
1239 TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
1240 CPLFree(m_psGarbageBlocksFirst);
1241 m_psGarbageBlocksFirst = psNext;
1242 }
1243 m_psGarbageBlocksLast = NULL;
1244 }
1245
1246 /**********************************************************************
1247 * TABBinBlockManager::PushGarbageBlockAsFirst()
1248 *
1249 * Insert a garbage block at the head of the list of garbage blocks.
1250 **********************************************************************/
PushGarbageBlockAsFirst(GInt32 nBlockPtr)1251 void TABBinBlockManager::PushGarbageBlockAsFirst(GInt32 nBlockPtr)
1252 {
1253 TABBlockRef *psNewBlockRef = (TABBlockRef *)CPLMalloc(sizeof(TABBlockRef));
1254
1255 psNewBlockRef->nBlockPtr = nBlockPtr;
1256 psNewBlockRef->psPrev = NULL;
1257 psNewBlockRef->psNext = m_psGarbageBlocksFirst;
1258
1259 if( m_psGarbageBlocksFirst != NULL )
1260 m_psGarbageBlocksFirst->psPrev = psNewBlockRef;
1261 m_psGarbageBlocksFirst = psNewBlockRef;
1262 if( m_psGarbageBlocksLast == NULL )
1263 m_psGarbageBlocksLast = m_psGarbageBlocksFirst;
1264 }
1265
1266 /**********************************************************************
1267 * TABBinBlockManager::PushGarbageBlockAsLast()
1268 *
1269 * Insert a garbage block at the tail of the list of garbage blocks.
1270 **********************************************************************/
PushGarbageBlockAsLast(GInt32 nBlockPtr)1271 void TABBinBlockManager::PushGarbageBlockAsLast(GInt32 nBlockPtr)
1272 {
1273 TABBlockRef *psNewBlockRef = (TABBlockRef *)CPLMalloc(sizeof(TABBlockRef));
1274
1275 psNewBlockRef->nBlockPtr = nBlockPtr;
1276 psNewBlockRef->psPrev = m_psGarbageBlocksLast;
1277 psNewBlockRef->psNext = NULL;
1278
1279 if( m_psGarbageBlocksLast != NULL )
1280 m_psGarbageBlocksLast->psNext = psNewBlockRef;
1281 m_psGarbageBlocksLast = psNewBlockRef;
1282 if( m_psGarbageBlocksFirst == NULL )
1283 m_psGarbageBlocksFirst = m_psGarbageBlocksLast;
1284 }
1285
1286 /**********************************************************************
1287 * TABBinBlockManager::GetFirstGarbageBlock()
1288 *
1289 * Return address of the block at the head of the list of garbage blocks
1290 * or 0 if the list is empty.
1291 **********************************************************************/
GetFirstGarbageBlock()1292 GInt32 TABBinBlockManager::GetFirstGarbageBlock()
1293 {
1294 if (m_psGarbageBlocksFirst)
1295 return m_psGarbageBlocksFirst->nBlockPtr;
1296
1297 return 0;
1298 }
1299
1300 /**********************************************************************
1301 * TABBinBlockManager::PopGarbageBlock()
1302 *
1303 * Return address of the block at the head of the list of garbage blocks
1304 * and remove that block from the list.
1305 * Retuns 0 if the list is empty.
1306 **********************************************************************/
PopGarbageBlock()1307 GInt32 TABBinBlockManager::PopGarbageBlock()
1308 {
1309 GInt32 nBlockPtr = 0;
1310
1311 if (m_psGarbageBlocksFirst)
1312 {
1313 nBlockPtr = m_psGarbageBlocksFirst->nBlockPtr;
1314 TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
1315 CPLFree(m_psGarbageBlocksFirst);
1316 if( psNext != NULL )
1317 psNext->psPrev = NULL;
1318 else
1319 m_psGarbageBlocksLast = NULL;
1320 m_psGarbageBlocksFirst = psNext;
1321 }
1322
1323 return nBlockPtr;
1324 }
1325