1 /*
2  * Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
3  *
4  * Permission is hereby granted, free of charge, to any person or organization
5  * obtaining a copy of the software and accompanying documentation covered by
6  * this license (the "Software") to use, reproduce, display, distribute,
7  * execute, and transmit the Software, and to prepare derivative works of the
8  * Software, and to permit third-parties to whom the Software is furnished to
9  * do so, all subject to the following:
10  *
11  * The copyright notices in the Software and this entire statement, including
12  * the above license grant, this restriction and the following disclaimer,
13  * must be included in all copies of the Software, in whole or in part, and
14  * all derivative works of the Software, unless such copies or derivative
15  * works are solely in the form of machine-executable object code generated by
16  * a source language processor.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */
26 
27 #include <cstring>
28 
29 #include "CheckedFile.h"
30 #include "Packet.h"
31 
32 
33 using namespace e57;
34 
35 struct IndexPacket
36 {
37       static constexpr unsigned MAX_ENTRIES = 2048;
38 
39       const uint8_t     packetType = INDEX_PACKET;
40 
41       uint8_t     packetFlags = 0;    // flag bitfields
42       uint16_t    packetLogicalLengthMinus1 = 0;
43       uint16_t    entryCount = 0;
44       uint8_t     indexLevel = 0;
45       uint8_t     reserved1[9] = {};   // must be zero
46 
47       struct IndexPacketEntry
48       {
49             uint64_t    chunkRecordNumber = 0;
50             uint64_t    chunkPhysicalOffset = 0;
51       } entries[MAX_ENTRIES];
52 
53       void        verify(unsigned bufferLength = 0, uint64_t totalRecordCount = 0, uint64_t fileSize = 0) const;
54 
55 #ifdef E57_DEBUG
56       void        dump(int indent = 0, std::ostream& os = std::cout) const;
57 #endif
58 };
59 
60 struct EmptyPacketHeader
61 {
62       const uint8_t     packetType = EMPTY_PACKET;
63 
64       uint8_t     reserved1 = 0;     // must be zero
65       uint16_t    packetLogicalLengthMinus1 = 0;
66 
67       void        verify(unsigned bufferLength = 0) const; //???use
68 
69 #ifdef E57_DEBUG
70       void        dump(int indent = 0, std::ostream& os = std::cout) const;
71 #endif
72 };
73 
74 //=============================================================================
75 // PacketReadCache
76 
PacketReadCache(CheckedFile * cFile,unsigned packetCount)77 PacketReadCache::PacketReadCache(CheckedFile* cFile, unsigned packetCount)
78    : cFile_(cFile),
79      entries_(packetCount)
80 {
81    if (packetCount == 0)
82    {
83       throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "packetCount=" + toString(packetCount));
84    }
85 }
86 
lock(uint64_t packetLogicalOffset,char * & pkt)87 std::unique_ptr<PacketLock> PacketReadCache::lock( uint64_t packetLogicalOffset, char* &pkt )
88 {
89 #ifdef E57_MAX_VERBOSE
90    std::cout << "PacketReadCache::lock() called, packetLogicalOffset=" << packetLogicalOffset << std::endl;
91 #endif
92 
93    /// Only allow one locked packet at a time.
94    if ( lockCount_ > 0 )
95    {
96       throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "lockCount=" + toString(lockCount_));
97    }
98 
99    /// Offset can't be 0
100    if ( packetLogicalOffset == 0 )
101    {
102       throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "packetLogicalOffset=" + toString(packetLogicalOffset));
103    }
104 
105    /// Linear scan for matching packet offset in cache
106    for ( unsigned i = 0; i < entries_.size(); ++i )
107    {
108       auto  &entry = entries_[i];
109 
110       if (packetLogicalOffset == entry.logicalOffset_)
111       {
112          /// Found a match, so don't have to read anything
113 #ifdef E57_MAX_VERBOSE
114          std::cout << "  Found matching cache entry, index=" << i << std::endl;
115 #endif
116          /// Mark entry with current useCount (keeps track of age of entry).
117          entry.lastUsed_ = ++useCount_;
118 
119          /// Publish buffer address to caller
120          pkt = entry.buffer_;
121 
122          /// Create lock so we are sure that we will be unlocked when use is finished.
123          std::unique_ptr<PacketLock> plock( new PacketLock( this, i ) );
124 
125          /// Increment cache lock just before return
126          ++lockCount_;
127 
128          return plock;
129       }
130    }
131    /// Get here if didn't find a match already in cache.
132 
133    /// Find least recently used (LRU) packet buffer
134    unsigned oldestEntry = 0;
135    unsigned oldestUsed = entries_.at(0).lastUsed_;
136 
137    for ( unsigned i = 0; i < entries_.size(); ++i )
138    {
139       const auto  &entry = entries_[i];
140 
141       if ( entry.lastUsed_ < oldestUsed )
142       {
143          oldestEntry = i;
144          oldestUsed = entry.lastUsed_;
145       }
146    }
147 #ifdef E57_MAX_VERBOSE
148    std::cout << "  Oldest entry=" << oldestEntry << " lastUsed=" << oldestUsed << std::endl;
149 #endif
150 
151    readPacket(oldestEntry, packetLogicalOffset);
152 
153    /// Publish buffer address to caller
154    pkt = entries_[oldestEntry].buffer_;
155 
156    /// Create lock so we are sure we will be unlocked when use is finished.
157    std::unique_ptr<PacketLock> plock( new PacketLock( this, oldestEntry ) );
158 
159    /// Increment cache lock just before return
160    ++lockCount_;
161 
162    return plock;
163 }
164 
unlock(unsigned lockedEntry)165 void PacketReadCache::unlock(unsigned lockedEntry)
166 {
167    //??? why lockedEntry not used?
168 #ifdef E57_MAX_VERBOSE
169    std::cout << "PacketReadCache::unlock() called, lockedEntry=" << lockedEntry << std::endl;
170 #endif
171 
172    if (lockCount_ != 1)
173    {
174       throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "lockCount=" + toString(lockCount_));
175    }
176 
177    --lockCount_;
178 }
179 
readPacket(unsigned oldestEntry,uint64_t packetLogicalOffset)180 void PacketReadCache::readPacket(unsigned oldestEntry, uint64_t packetLogicalOffset)
181 {
182 #ifdef E57_MAX_VERBOSE
183    std::cout << "PacketReadCache::readPacket() called, oldestEntry=" << oldestEntry << " packetLogicalOffset=" << packetLogicalOffset << std::endl;
184 #endif
185 
186    /// Read header of packet first to get length.  Use EmptyPacketHeader since it has the commom fields to all packets.
187    EmptyPacketHeader header;
188 
189    cFile_->seek(packetLogicalOffset, CheckedFile::Logical);
190    cFile_->read(reinterpret_cast<char*>(&header), sizeof(header));
191 
192    /// Can't verify packet header here, because it is not really an EmptyPacketHeader.
193    unsigned packetLength = header.packetLogicalLengthMinus1+1;
194 
195    /// Be paranoid about packetLength before read
196    if (packetLength > DATA_PACKET_MAX)
197    {
198       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
199    }
200 
201    auto  &entry = entries_.at(oldestEntry);
202 
203    /// Now read in whole packet into preallocated buffer_.  Note buffer is
204    cFile_->seek(packetLogicalOffset, CheckedFile::Logical);
205    cFile_->read(entry.buffer_, packetLength);
206 
207    /// Verify that packet is good.
208    switch (header.packetType)
209    {
210       case DATA_PACKET: {
211          auto dpkt = reinterpret_cast<DataPacket*>(entry.buffer_);
212 
213          dpkt->verify(packetLength);
214 #ifdef E57_MAX_VERBOSE
215          std::cout << "  data packet:" << std::endl;
216          dpkt->dump(4); //???
217 #endif
218       }
219          break;
220       case INDEX_PACKET: {
221          auto ipkt = reinterpret_cast<IndexPacket*>(entry.buffer_);
222 
223          ipkt->verify(packetLength);
224 #ifdef E57_MAX_VERBOSE
225          std::cout << "  index packet:" << std::endl;
226          ipkt->dump(4); //???
227 #endif
228       }
229          break;
230       case EMPTY_PACKET: {
231          auto hp = reinterpret_cast<EmptyPacketHeader*>(entry.buffer_);
232 
233          hp->verify(packetLength);
234 #ifdef E57_MAX_VERBOSE
235          std::cout << "  empty packet:" << std::endl;
236          hp->dump(4); //???
237 #endif
238       }
239          break;
240       default:
241          throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "packetType=" + toString(header.packetType));
242    }
243 
244    entry.logicalOffset_ = packetLogicalOffset;
245 
246    /// Mark entry with current useCount (keeps track of age of entry).
247    /// This is a cache, so a small hiccup when useCount_ overflows won't hurt.
248    entry.lastUsed_ = ++useCount_;
249 }
250 
251 #ifdef E57_DEBUG
dump(int indent,std::ostream & os)252 void PacketReadCache::dump(int indent, std::ostream& os)
253 {
254    os << space(indent) << "lockCount: " << lockCount_ << std::endl;
255    os << space(indent) << "useCount:  " << useCount_ << std::endl;
256    os << space(indent) << "entries:" << std::endl;
257    for (unsigned i=0; i < entries_.size(); i++) {
258       os << space(indent) << "entry[" << i << "]:" << std::endl;
259       os << space(indent+4) << "logicalOffset:  " << entries_[i].logicalOffset_ << std::endl;
260       os << space(indent+4) << "lastUsed:        " << entries_[i].lastUsed_ << std::endl;
261       if (entries_[i].logicalOffset_ != 0) {
262          os << space(indent+4) << "packet:" << std::endl;
263          switch (reinterpret_cast<EmptyPacketHeader*>(entries_.at(i).buffer_)->packetType) {
264             case DATA_PACKET: {
265                auto dpkt = reinterpret_cast<DataPacket*>(entries_.at(i).buffer_);
266                dpkt->dump(indent+6, os);
267             }
268                break;
269             case INDEX_PACKET: {
270                auto ipkt = reinterpret_cast<IndexPacket*>(entries_.at(i).buffer_);
271                ipkt->dump(indent+6, os);
272             }
273                break;
274             case EMPTY_PACKET: {
275                auto hp = reinterpret_cast<EmptyPacketHeader*>(entries_.at(i).buffer_);
276                hp->dump(indent+6, os);
277             }
278                break;
279             default:
280                throw E57_EXCEPTION2(E57_ERROR_INTERNAL,
281                                     "packetType=" + toString(reinterpret_cast<EmptyPacketHeader*>(entries_.at(i).buffer_)->packetType));
282          }
283       }
284    }
285 }
286 #endif
287 
288 //=============================================================================
289 // PacketLock
290 
PacketLock(PacketReadCache * cache,unsigned cacheIndex)291 PacketLock::PacketLock(PacketReadCache* cache, unsigned cacheIndex)
292    : cache_(cache),
293      cacheIndex_(cacheIndex)
294 {
295 #ifdef E57_MAX_VERBOSE
296    std::cout << "PacketLock() called" << std::endl;
297 #endif
298 }
299 
~PacketLock()300 PacketLock::~PacketLock()
301 {
302 #ifdef E57_MAX_VERBOSE
303    std::cout << "~PacketLock() called" << std::endl;
304 #endif
305    try {
306       /// Note cache must live longer than lock, this is reasonable assumption.
307       cache_->unlock(cacheIndex_);
308    } catch (...) {
309       //??? report?
310    }
311 }
312 
313 //=============================================================================
314 // DataPacketHeader
315 
DataPacketHeader()316 DataPacketHeader::DataPacketHeader()
317 {
318    /// Double check that packet struct is correct length.  Watch out for RTTI increasing the size.
319    static_assert( sizeof( DataPacketHeader ) == 6, "Unexpected size of DataPacketHeader" );
320 }
321 
reset()322 void DataPacketHeader::reset()
323 {
324    packetFlags = 0;
325    packetLogicalLengthMinus1 = 0;
326    bytestreamCount = 0;
327 }
328 
verify(unsigned bufferLength) const329 void DataPacketHeader::verify(unsigned bufferLength) const
330 {
331    /// Verify that packet is correct type
332    if (packetType != DATA_PACKET)
333    {
334       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetType=" + toString(packetType));
335    }
336 
337    /// ??? check reserved flags zero?
338 
339    /// Check packetLength is at least large enough to hold header
340    unsigned packetLength = packetLogicalLengthMinus1+1;
341    if (packetLength < sizeof(*this))
342       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
343 
344    /// Check packet length is multiple of 4
345    if (packetLength % 4)
346       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
347 
348    /// Check actual packet length is large enough.
349    if (bufferLength > 0 && packetLength > bufferLength) {
350       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
351                            "packetLength=" + toString(packetLength)
352                            + " bufferLength=" + toString(bufferLength));
353    }
354 
355    /// Make sure there is at least one entry in packet  ??? 0 record cvect allowed?
356    if (bytestreamCount == 0)
357       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "bytestreamCount=" + toString(bytestreamCount));
358 
359    /// Check packet is at least long enough to hold bytestreamBufferLength array
360    if (sizeof(DataPacketHeader) + 2*bytestreamCount > packetLength) {
361       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
362                            "packetLength=" + toString(packetLength)
363                            + " bytestreamCount=" + toString(bytestreamCount));
364    }
365 }
366 
367 #ifdef E57_DEBUG
dump(int indent,std::ostream & os) const368 void DataPacketHeader::dump(int indent, std::ostream& os) const
369 {
370    os << space(indent) << "packetType:                " << static_cast<unsigned>(packetType) << std::endl;
371    os << space(indent) << "packetFlags:               " << static_cast<unsigned>(packetFlags) << std::endl;
372    os << space(indent) << "packetLogicalLengthMinus1: " << packetLogicalLengthMinus1 << std::endl;
373    os << space(indent) << "bytestreamCount:           " << bytestreamCount << std::endl;
374 }
375 #endif
376 
377 //=============================================================================
378 // DataPacket
379 
DataPacket()380 DataPacket::DataPacket()
381 {
382    /// Double check that packet struct is correct length.  Watch out for RTTI increasing the size.
383    static_assert( sizeof( DataPacket ) == 64*1024, "Unexpected size of DataPacket" );
384 }
385 
verify(unsigned bufferLength) const386 void DataPacket::verify(unsigned bufferLength) const
387 {
388    //??? do all packets need versions?  how extend without breaking older checking?  need to check file version#?
389 
390    /// Verify header is good
391    auto hp = reinterpret_cast<const DataPacketHeader*>(this);
392 
393    hp->verify(bufferLength);
394 
395    /// Calc sum of lengths of each bytestream buffer in this packet
396    auto bsbLength = reinterpret_cast<const uint16_t*>(&payload[0]);
397    unsigned totalStreamByteCount = 0;
398 
399    for (unsigned i=0; i < header.bytestreamCount; i++)
400    {
401       totalStreamByteCount += bsbLength[i];
402    }
403 
404    /// Calc size of packet needed
405    const unsigned packetLength = header.packetLogicalLengthMinus1+1;
406    const unsigned needed = sizeof(DataPacketHeader) + 2*header.bytestreamCount + totalStreamByteCount;
407 #ifdef E57_MAX_VERBOSE
408    std::cout << "needed=" << needed << " actual=" << packetLength << std::endl; //???
409 #endif
410 
411    /// If needed is not with 3 bytes of actual packet size, have an error
412    if (needed > packetLength || needed+3 < packetLength)
413    {
414       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
415                            "needed=" + toString(needed)
416                            + "packetLength=" + toString(packetLength));
417    }
418 
419    /// Verify that padding at end of packet is zero
420    for (unsigned i=needed; i < packetLength; i++)
421    {
422       if (reinterpret_cast<const char*>(this)[i] != 0)
423       {
424          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "i=" + toString(i));
425       }
426    }
427 }
428 
getBytestream(unsigned bytestreamNumber,unsigned & byteCount)429 char* DataPacket::getBytestream(unsigned bytestreamNumber, unsigned& byteCount)
430 {
431 #ifdef E57_MAX_VERBOSE
432    std::cout << "getBytestream called, bytestreamNumber=" << bytestreamNumber << std::endl;
433 #endif
434 
435    /// Verify that packet is correct type
436    if (header.packetType != DATA_PACKET)
437    {
438       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetType=" + toString(header.packetType));
439    }
440 
441    /// Check bytestreamNumber in bounds
442    if (bytestreamNumber >= header.bytestreamCount)
443    {
444       throw E57_EXCEPTION2(E57_ERROR_INTERNAL,
445                            "bytestreamNumber=" + toString(bytestreamNumber)
446                            + "bytestreamCount=" + toString(header.bytestreamCount));
447    }
448 
449    /// Calc positions in packet
450    auto bsbLength = reinterpret_cast<uint16_t*>(&payload[0]);
451    auto streamBase = reinterpret_cast<char*>(&bsbLength[header.bytestreamCount]);
452 
453    /// Sum size of preceeding stream buffers to get position
454    unsigned totalPreceeding = 0;
455    for (unsigned i=0; i < bytestreamNumber; i++)
456       totalPreceeding += bsbLength[i];
457 
458    byteCount = bsbLength[bytestreamNumber];
459 
460    /// Double check buffer is completely within packet
461    if (sizeof(DataPacketHeader) + 2*header.bytestreamCount + totalPreceeding + byteCount > header.packetLogicalLengthMinus1 + 1U)
462    {
463       throw E57_EXCEPTION2(E57_ERROR_INTERNAL,
464                            "bytestreamCount=" + toString(header.bytestreamCount)
465                            + " totalPreceeding=" + toString(totalPreceeding)
466                            + " byteCount=" + toString(byteCount)
467                            + " packetLogicalLengthMinus1=" + toString(header.packetLogicalLengthMinus1));
468    }
469 
470    /// Return start of buffer
471    return(&streamBase[totalPreceeding]);
472 }
473 
getBytestreamBufferLength(unsigned bytestreamNumber)474 unsigned DataPacket::getBytestreamBufferLength(unsigned bytestreamNumber)
475 {
476    //??? for now:
477    unsigned byteCount;
478    (void) getBytestream(bytestreamNumber, byteCount);
479    return(byteCount);
480 }
481 
482 #ifdef E57_DEBUG
dump(int indent,std::ostream & os) const483 void DataPacket::dump(int indent, std::ostream& os) const
484 {
485    if (header.packetType != DATA_PACKET)
486    {
487       throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "packetType=" + toString(header.packetType));
488    }
489 
490    reinterpret_cast<const DataPacketHeader*>(this)->dump(indent, os);
491 
492    auto bsbLength = reinterpret_cast<const uint16_t*>(&payload[0]);
493    auto p = reinterpret_cast<const uint8_t*>(&bsbLength[header.bytestreamCount]);
494 
495    for (unsigned i=0; i < header.bytestreamCount; i++)
496    {
497       os << space(indent) << "bytestream[" << i << "]:" << std::endl;
498       os << space(indent+4) << "length: " << bsbLength[i] << std::endl;
499       /*====
500            unsigned j;
501            for (j=0; j < bsbLength[i] && j < 10; j++)
502                os << space(indent+4) << "byte[" << j << "]=" << (unsigned)p[j] << endl;
503            if (j < bsbLength[i])
504                os << space(indent+4) << bsbLength[i]-j << " more unprinted..." << endl;
505    ====*/
506       p += bsbLength[i];
507       if (p - reinterpret_cast<const uint8_t*>(this) > DATA_PACKET_MAX)
508       {
509          throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "size=" + toString(p - reinterpret_cast<const uint8_t*>(this)));
510       }
511    }
512 }
513 #endif
514 
515 //=============================================================================
516 // IndexPacket
517 
verify(unsigned bufferLength,uint64_t totalRecordCount,uint64_t fileSize) const518 void IndexPacket::verify(unsigned bufferLength, uint64_t totalRecordCount, uint64_t fileSize) const
519 {
520    //??? do all packets need versions?  how extend without breaking older checking?  need to check file version#?
521 
522    /// Verify that packet is correct type
523    if (packetType != INDEX_PACKET)
524       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetType=" + toString(packetType));
525 
526    /// Check packetLength is at least large enough to hold header
527    unsigned packetLength = packetLogicalLengthMinus1+1;
528    if (packetLength < sizeof(*this))
529       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
530 
531    /// Check packet length is multiple of 4
532    if (packetLength % 4)
533       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
534 
535    /// Make sure there is at least one entry in packet  ??? 0 record cvect allowed?
536    if (entryCount == 0)
537       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "entryCount=" + toString(entryCount));
538 
539    /// Have to have <= 2048 entries
540    if (entryCount > MAX_ENTRIES)
541       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "entryCount=" + toString(entryCount));
542 
543    /// Index level should be <= 5.  Because (5+1)* 11 bits = 66 bits, which will cover largest number of chunks possible.
544    if (indexLevel > 5)
545       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "indexLevel=" + toString(indexLevel));
546 
547    /// Index packets above level 0 must have at least two entries (otherwise no point to existing).
548    ///??? check that this is in spec
549    if (indexLevel > 0 && entryCount < 2)
550       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "indexLevel=" + toString(indexLevel) + " entryCount=" + toString(entryCount));
551 
552    /// If not later version, verify reserved fields are zero. ??? test file version
553    /// if (version <= E57_FORMAT_MAJOR) { //???
554    for (unsigned i=0; i < sizeof(reserved1); i++) {
555       if (reserved1[i] != 0)
556          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "i=" + toString(i));
557    }
558 
559    /// Check actual packet length is large enough.
560    if (bufferLength > 0 && packetLength > bufferLength) {
561       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
562                            "packetLength=" + toString(packetLength)
563                            + " bufferLength=" + toString(bufferLength));
564    }
565 
566    /// Check if entries will fit in space provided
567    unsigned neededLength = 16 + 8*entryCount;
568    if (packetLength < neededLength) {
569       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
570                            "packetLength=" + toString(packetLength)
571                            + " neededLength=" + toString(neededLength));
572    }
573 
574 #ifdef E57_MAX_DEBUG
575    /// Verify padding at end is zero.
576    const char* p = reinterpret_cast<const char*>(this);
577    for (unsigned i=neededLength; i < packetLength; i++) {
578       if (p[i] != 0)
579          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "i=" + toString(i));
580    }
581 
582    /// Verify records and offsets are in sorted order
583    for (unsigned i=0; i < entryCount; i++) {
584       /// Check chunkRecordNumber is in bounds
585       if (totalRecordCount > 0 && entries[i].chunkRecordNumber >= totalRecordCount) {
586          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
587                               "i=" + toString(i)
588                               + " chunkRecordNumber=" + toString(entries[i].chunkRecordNumber)
589                               + " totalRecordCount=" + toString(totalRecordCount));
590       }
591 
592       /// Check record numbers are strictly increasing
593       if (i > 0 && entries[i-1].chunkRecordNumber >= entries[i].chunkRecordNumber) {
594          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
595                               "i=" + toString(i)
596                               + " prevChunkRecordNumber=" + toString(entries[i-1].chunkRecordNumber)
597                + " currentChunkRecordNumber=" + toString(entries[i].chunkRecordNumber));
598       }
599 
600       /// Check chunkPhysicalOffset is in bounds
601       if (fileSize > 0 && entries[i].chunkPhysicalOffset >= fileSize) {
602          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
603                               "i=" + toString(i)
604                               + " chunkPhysicalOffset=" + toString(entries[i].chunkPhysicalOffset)
605                               + " fileSize=" + toString(fileSize));
606       }
607 
608       /// Check chunk offsets are strictly increasing
609       if (i > 0 && entries[i-1].chunkPhysicalOffset >= entries[i].chunkPhysicalOffset) {
610          throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
611                               "i=" + toString(i)
612                               + " prevChunkPhysicalOffset=" + toString(entries[i-1].chunkPhysicalOffset)
613                + " currentChunkPhysicalOffset=" + toString(entries[i].chunkPhysicalOffset));
614       }
615    }
616 #endif
617 }
618 
619 #ifdef E57_DEBUG
dump(int indent,std::ostream & os) const620 void IndexPacket::dump(int indent, std::ostream& os) const
621 {
622    os << space(indent) << "packetType:                " << static_cast<unsigned>(packetType) << std::endl;
623    os << space(indent) << "packetFlags:               " << static_cast<unsigned>(packetFlags) << std::endl;
624    os << space(indent) << "packetLogicalLengthMinus1: " << packetLogicalLengthMinus1 << std::endl;
625    os << space(indent) << "entryCount:                " << entryCount << std::endl;
626    os << space(indent) << "indexLevel:                " << indexLevel << std::endl;
627    unsigned i;
628    for (i=0; i < entryCount && i < 10; i++) {
629       os << space(indent) << "entry[" << i << "]:" << std::endl;
630       os << space(indent+4) << "chunkRecordNumber:    " << entries[i].chunkRecordNumber << std::endl;
631       os << space(indent+4) << "chunkPhysicalOffset:  " << entries[i].chunkPhysicalOffset << std::endl;
632    }
633    if (i < entryCount)
634    {
635       os << space(indent) << entryCount-i << "more entries unprinted..." << std::endl;
636    }
637 }
638 #endif
639 
640 //=============================================================================
641 // EmptyPacketHeader
642 
verify(unsigned bufferLength) const643 void EmptyPacketHeader::verify(unsigned bufferLength) const
644 {
645    /// Verify that packet is correct type
646    if (packetType != EMPTY_PACKET)
647       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetType=" + toString(packetType));
648 
649    /// Check packetLength is at least large enough to hold header
650    unsigned packetLength = packetLogicalLengthMinus1+1;
651    if (packetLength < sizeof(*this))
652       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
653 
654    /// Check packet length is multiple of 4
655    if (packetLength % 4)
656       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET, "packetLength=" + toString(packetLength));
657 
658    /// Check actual packet length is large enough.
659    if (bufferLength > 0 && packetLength > bufferLength) {
660       throw E57_EXCEPTION2(E57_ERROR_BAD_CV_PACKET,
661                            "packetLength=" + toString(packetLength)
662                            + " bufferLength=" + toString(bufferLength));
663    }
664 }
665 
666 #ifdef E57_DEBUG
dump(int indent,std::ostream & os) const667 void EmptyPacketHeader::dump(int indent, std::ostream& os) const
668 {
669    os << space(indent) << "packetType:                " << static_cast<unsigned>(packetType) << std::endl;
670    os << space(indent) << "packetLogicalLengthMinus1: " << packetLogicalLengthMinus1 << std::endl;
671 }
672 #endif
673