diff --git a/src/CompressedVectorWriterImpl.cpp b/src/CompressedVectorWriterImpl.cpp index f7efff7..cd9082a 100644 --- a/src/CompressedVectorWriterImpl.cpp +++ b/src/CompressedVectorWriterImpl.cpp @@ -290,6 +290,12 @@ namespace e57 checkImageFileOpen( __FILE__, __LINE__, static_cast( __FUNCTION__ ) ); checkWriterOpen( __FILE__, __LINE__, static_cast( __FUNCTION__ ) ); + if ( requestedRecordCount == 0 ) + { + packetWriteZeroRecords(); + return; + } + // Check that requestedRecordCount is not larger than the sbufs if ( requestedRecordCount > sbufs_.at( 0 ).impl()->capacity() ) { @@ -610,6 +616,51 @@ namespace e57 return ( packetPhysicalOffset ); //??? needed } + // If we don't have any records, write a packet which is only the header + zero padding. + // Code is a simplified version of packetWrite(). + void CompressedVectorWriterImpl::packetWriteZeroRecords() + { + ImageFileImplSharedPtr imf( cVector_->destImageFile_ ); + + dataPacket_.header.reset(); + + // Use temp buf in object (is 64KBytes long) instead of allocating each time here + char *packet = reinterpret_cast( &dataPacket_ ); + + auto packetLength = sizeof( DataPacketHeader ); + + // packetLength must be multiple of 4, add zero padding + auto data = reinterpret_cast( &packet[sizeof( DataPacketHeader )] ); + while ( packetLength % 4 ) + { + *data++ = 0; + + packetLength++; + } + + // Prepare header in dataPacket_, now that we are sure of packetLength + dataPacket_.header.packetLogicalLengthMinus1 = static_cast( packetLength - 1 ); + + // Double check that data packet is well formed + dataPacket_.verify( packetLength ); + + // Write packet at beginning of free space in file + uint64_t packetLogicalOffset = imf->allocateSpace( packetLength, false ); + uint64_t packetPhysicalOffset = imf->file_->logicalToPhysical( packetLogicalOffset ); + + imf->file_->seek( packetLogicalOffset ); + imf->file_->write( packet, packetLength ); + + // If first data packet written for this CompressedVector binary section, + // save address to put in section header + if ( dataPacketsCount_ == 0 ) + { + dataPhysicalOffset_ = packetPhysicalOffset; + } + + dataPacketsCount_++; + } + void CompressedVectorWriterImpl::flush() { for ( auto &bytestream : bytestreams_ ) diff --git a/src/CompressedVectorWriterImpl.h b/src/CompressedVectorWriterImpl.h index 80f6d68..6fa2760 100644 --- a/src/CompressedVectorWriterImpl.h +++ b/src/CompressedVectorWriterImpl.h @@ -57,6 +57,8 @@ namespace e57 size_t totalOutputAvailable() const; size_t currentPacketSize() const; uint64_t packetWrite(); + void packetWriteZeroRecords(); + void flush(); std::vector sbufs_; diff --git a/src/Packet.cpp b/src/Packet.cpp index cdfef47..960cb42 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -354,6 +354,8 @@ void DataPacketHeader::reset() void DataPacketHeader::verify( unsigned bufferLength ) const { // Verify that packet is correct type + // cppcheck-suppress knownConditionTrueFalse; (data is read as a blob, so the const might not + // be valid) if ( packetType != DATA_PACKET ) { throw E57_EXCEPTION2( ErrorBadCVPacket, @@ -382,9 +384,8 @@ void DataPacketHeader::verify( unsigned bufferLength ) const " bufferLength=" + toString( bufferLength ) ); } - // Make sure there is at least one entry in packet ??? 0 record cvect - // allowed? - if ( bytestreamCount == 0 ) + // Make sure there is at least one entry in packet + if ( hasRecords() && ( bytestreamCount == 0 ) ) { throw E57_EXCEPTION2( ErrorBadCVPacket, "DATA; bytestreamCount=" + toString( bytestreamCount ) ); @@ -482,7 +483,7 @@ char *DataPacket::getBytestream( unsigned bytestreamNumber, unsigned &byteCount { throw E57_EXCEPTION2( ErrorInternal, "bytestreamNumber=" + toString( bytestreamNumber ) + - "bytestreamCount=" + toString( header.bytestreamCount ) ); + " bytestreamCount=" + toString( header.bytestreamCount ) ); } // Calc positions in packet diff --git a/src/Packet.h b/src/Packet.h index 722ce5e..010890b 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -107,7 +107,15 @@ namespace e57 void reset(); - void verify( unsigned bufferLength = 0 ) const; //???use + void verify( unsigned bufferLength = 0 ) const; + + // Does this packet have any records? + bool hasRecords() const + { + // If the packet does not have records, the logical length will be 8 bytes (the length of + // the header padded to 8-byte boundary). + return packetLogicalLengthMinus1 > 7; + } #ifdef E57_ENABLE_DIAGNOSTIC_OUTPUT void dump( int indent = 0, std::ostream &os = std::cout ) const; diff --git a/test/src/test_SimpleWriter.cpp b/test/src/test_SimpleWriter.cpp index ddd69d7..f41de4b 100644 --- a/test/src/test_SimpleWriter.cpp +++ b/test/src/test_SimpleWriter.cpp @@ -148,6 +148,34 @@ TEST( SimpleWriter, Empty ) E57_ASSERT_NO_THROW( e57::Writer writer( "./empty.e57", options ) ); } +TEST( SimpleWriter, ZeroPoints ) +{ + e57::WriterOptions options; + options.guid = "Zero Points GUID"; + + e57::Writer *writer = nullptr; + + E57_ASSERT_NO_THROW( writer = new e57::Writer( "./ZeroPoints.e57", options ) ); + + constexpr uint16_t cNumPoints = 0; + + e57::Data3D header; + header.guid = "Zero Points Header GUID"; + header.pointCount = cNumPoints; + + // For a valid file, we need to specify cartesian or spherical. + header.pointFields.cartesianXField = true; + header.pointFields.cartesianYField = true; + header.pointFields.cartesianZField = true; + header.cartesianBounds.xMinimum = 0.0; + + e57::Data3DPointsDouble pointsData( header ); + + E57_ASSERT_NO_THROW( writer->WriteData3DData( header, pointsData ) ); + + delete writer; +} + TEST( SimpleWriter, InvalidData3DValueCartesian ) { e57::WriterOptions options;