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