meshlab/src/external/e57/src/Encoder.cpp
2021-03-29 15:28:00 +02:00

974 lines
34 KiB
C++

/*
* Original work Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
* Modified work Copyright 2018 - 2020 Andy Maloney <asmaloney@gmail.com>
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cstring>
#include "CompressedVectorNodeImpl.h"
#include "Encoder.h"
#include "FloatNodeImpl.h"
#include "ImageFileImpl.h"
#include "IntegerNodeImpl.h"
#include "Packet.h"
#include "ScaledIntegerNodeImpl.h"
#include "SourceDestBufferImpl.h"
using namespace e57;
std::shared_ptr<Encoder> Encoder::EncoderFactory( unsigned bytestreamNumber,
std::shared_ptr<CompressedVectorNodeImpl> cVector,
std::vector<SourceDestBuffer> &sbufs, ustring & /*codecPath*/ )
{
//??? For now, only handle one input
if ( sbufs.size() != 1 )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "sbufsSize=" + toString( sbufs.size() ) );
}
SourceDestBuffer sbuf = sbufs.at( 0 );
/// Get node we are going to encode from the CompressedVector's prototype
NodeImplSharedPtr prototype = cVector->getPrototype();
ustring path = sbuf.pathName();
NodeImplSharedPtr encodeNode = prototype->get( path );
#ifdef E57_MAX_VERBOSE
std::cout << "Node to encode:" << std::endl; //???
encodeNode->dump( 2 );
#endif
switch ( encodeNode->type() )
{
case E57_INTEGER:
{
std::shared_ptr<IntegerNodeImpl> ini =
std::static_pointer_cast<IntegerNodeImpl>( encodeNode ); // downcast to correct type
/// Get pointer to parent ImageFileImpl, to call bitsNeeded()
ImageFileImplSharedPtr imf( encodeNode->destImageFile_ ); //??? should be function for this,
// imf->parentFile()
//--> ImageFile?
unsigned bitsPerRecord = imf->bitsNeeded( ini->minimum(), ini->maximum() );
//!!! need to pick smarter channel buffer sizes, here and elsewhere
/// Constuct Integer encoder with appropriate register size, based on
/// number of bits stored.
if ( bitsPerRecord == 0 )
{
std::shared_ptr<Encoder> encoder( new ConstantIntegerEncoder( bytestreamNumber, sbuf, ini->minimum() ) );
return encoder;
}
if ( bitsPerRecord <= 8 )
{
std::shared_ptr<Encoder> encoder( new BitpackIntegerEncoder<uint8_t>(
false, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/, ini->minimum(), ini->maximum(), 1.0, 0.0 ) );
return encoder;
}
if ( bitsPerRecord <= 16 )
{
std::shared_ptr<Encoder> encoder( new BitpackIntegerEncoder<uint16_t>(
false, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/, ini->minimum(), ini->maximum(), 1.0, 0.0 ) );
return encoder;
}
if ( bitsPerRecord <= 32 )
{
std::shared_ptr<Encoder> encoder( new BitpackIntegerEncoder<uint32_t>(
false, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/, ini->minimum(), ini->maximum(), 1.0, 0.0 ) );
return encoder;
}
std::shared_ptr<Encoder> encoder( new BitpackIntegerEncoder<uint64_t>(
false, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/, ini->minimum(), ini->maximum(), 1.0, 0.0 ) );
return encoder;
}
case E57_SCALED_INTEGER:
{
std::shared_ptr<ScaledIntegerNodeImpl> sini =
std::static_pointer_cast<ScaledIntegerNodeImpl>( encodeNode ); // downcast to correct type
/// Get pointer to parent ImageFileImpl, to call bitsNeeded()
ImageFileImplSharedPtr imf( encodeNode->destImageFile_ ); //??? should be function for this,
// imf->parentFile()
//--> ImageFile?
unsigned bitsPerRecord = imf->bitsNeeded( sini->minimum(), sini->maximum() );
//!!! need to pick smarter channel buffer sizes, here and elsewhere
/// Constuct ScaledInteger encoder with appropriate register size,
/// based on number of bits stored.
if ( bitsPerRecord == 0 )
{
std::shared_ptr<Encoder> encoder( new ConstantIntegerEncoder( bytestreamNumber, sbuf, sini->minimum() ) );
return encoder;
}
if ( bitsPerRecord <= 8 )
{
std::shared_ptr<Encoder> encoder(
new BitpackIntegerEncoder<uint8_t>( true, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/,
sini->minimum(), sini->maximum(), sini->scale(), sini->offset() ) );
return encoder;
}
if ( bitsPerRecord <= 16 )
{
std::shared_ptr<Encoder> encoder(
new BitpackIntegerEncoder<uint16_t>( true, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/,
sini->minimum(), sini->maximum(), sini->scale(), sini->offset() ) );
return encoder;
}
if ( bitsPerRecord <= 32 )
{
std::shared_ptr<Encoder> encoder(
new BitpackIntegerEncoder<uint32_t>( true, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/,
sini->minimum(), sini->maximum(), sini->scale(), sini->offset() ) );
return encoder;
}
std::shared_ptr<Encoder> encoder(
new BitpackIntegerEncoder<uint64_t>( true, bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/, sini->minimum(),
sini->maximum(), sini->scale(), sini->offset() ) );
return encoder;
}
case E57_FLOAT:
{
std::shared_ptr<FloatNodeImpl> fni =
std::static_pointer_cast<FloatNodeImpl>( encodeNode ); // downcast to correct type
//!!! need to pick smarter channel buffer sizes, here and elsewhere
std::shared_ptr<Encoder> encoder(
new BitpackFloatEncoder( bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/, fni->precision() ) );
return encoder;
}
case E57_STRING:
{
std::shared_ptr<Encoder> encoder(
new BitpackStringEncoder( bytestreamNumber, sbuf, DATA_PACKET_MAX /*!!!*/ ) );
return encoder;
}
default:
{
throw E57_EXCEPTION2( E57_ERROR_BAD_PROTOTYPE, "nodeType=" + toString( encodeNode->type() ) );
}
}
}
Encoder::Encoder( unsigned bytestreamNumber ) : bytestreamNumber_( bytestreamNumber )
{
}
#ifdef E57_DEBUG
void Encoder::dump( int indent, std::ostream &os ) const
{
os << space( indent ) << "bytestreamNumber: " << bytestreamNumber_ << std::endl;
}
#endif
///================
BitpackEncoder::BitpackEncoder( unsigned bytestreamNumber, SourceDestBuffer &sbuf, unsigned outputMaxSize,
unsigned alignmentSize ) :
Encoder( bytestreamNumber ),
sourceBuffer_( sbuf.impl() ), outBuffer_( outputMaxSize ), outBufferFirst_( 0 ), outBufferEnd_( 0 ),
outBufferAlignmentSize_( alignmentSize ), currentRecordIndex_( 0 )
{
}
unsigned BitpackEncoder::sourceBufferNextIndex()
{
return ( sourceBuffer_->nextIndex() );
}
uint64_t BitpackEncoder::currentRecordIndex()
{
return ( currentRecordIndex_ );
}
size_t BitpackEncoder::outputAvailable() const
{
return outBufferEnd_ - outBufferFirst_;
}
void BitpackEncoder::outputRead( char *dest, const size_t byteCount )
{
#ifdef E57_MAX_VERBOSE
std::cout << "BitpackEncoder::outputRead() called, dest=" << dest << " byteCount=" << byteCount << std::endl; //???
#endif
/// Check we have enough bytes in queue
if ( byteCount > outputAvailable() )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "byteCount=" + toString( byteCount ) +
" outputAvailable=" + toString( outputAvailable() ) );
}
/// Copy output bytes to caller
memcpy( dest, &outBuffer_[outBufferFirst_], byteCount );
#ifdef E57_MAX_VERBOSE
{
unsigned i;
for ( i = 0; i < byteCount && i < 20; i++ )
{
std::cout << " outBuffer[" << outBufferFirst_ + i
<< "]=" << static_cast<unsigned>( static_cast<unsigned char>( outBuffer_[outBufferFirst_ + i] ) )
<< std::endl; //???
}
if ( i < byteCount )
{
std::cout << " " << byteCount - 1 << " bytes unprinted..." << std::endl;
}
}
#endif
/// Advance head pointer.
outBufferFirst_ += byteCount;
/// Don't slide remaining data down now, wait until do some more processing
/// (that's when data needs to be aligned).
}
void BitpackEncoder::outputClear()
{
outBufferFirst_ = 0;
outBufferEnd_ = 0;
}
void BitpackEncoder::sourceBufferSetNew( std::vector<SourceDestBuffer> &sbufs )
{
/// Verify that this encoder only has single input buffer
if ( sbufs.size() != 1 )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "sbufsSize=" + toString( sbufs.size() ) );
}
sourceBuffer_ = sbufs.at( 0 ).impl();
}
size_t BitpackEncoder::outputGetMaxSize()
{
/// Its size that matters here, not capacity
return ( outBuffer_.size() );
}
void BitpackEncoder::outputSetMaxSize( unsigned byteCount )
{
/// Ignore if trying to shrink buffer (queue might get messed up).
if ( byteCount > outBuffer_.size() )
{
outBuffer_.resize( byteCount );
}
}
void BitpackEncoder::outBufferShiftDown()
{
/// Move data down closer to beginning of outBuffer_.
/// But keep outBufferEnd_ as a multiple of outBufferAlignmentSize_.
/// This ensures that writes into buffer can occur on natural boundaries.
/// Otherwise some CPUs will fault.
if ( outBufferFirst_ == outBufferEnd_ )
{
/// Buffer is empty, reset indices to 0
outBufferFirst_ = 0;
outBufferEnd_ = 0;
return;
}
/// Round newEnd up to nearest multiple of outBufferAlignmentSize_.
size_t newEnd = outputAvailable();
size_t remainder = newEnd % outBufferAlignmentSize_;
if ( remainder > 0 )
{
newEnd += outBufferAlignmentSize_ - remainder;
}
size_t newFirst = outBufferFirst_ - ( outBufferEnd_ - newEnd );
size_t byteCount = outBufferEnd_ - outBufferFirst_;
/// Double check round up worked
if ( newEnd % outBufferAlignmentSize_ )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "newEnd=" + toString( newEnd ) +
" outBufferAlignmentSize=" + toString( outBufferAlignmentSize_ ) );
}
/// Be paranoid before memory copy
if ( newFirst + byteCount > outBuffer_.size() )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "newFirst=" + toString( newFirst ) +
" byteCount=" + toString( byteCount ) +
" outBufferSize=" + toString( outBuffer_.size() ) );
}
/// Move available data down closer to beginning of outBuffer_. Overlapping
/// regions ok with memmove().
memmove( &outBuffer_[newFirst], &outBuffer_[outBufferFirst_], byteCount );
/// Update indexes
outBufferFirst_ = newFirst;
outBufferEnd_ = newEnd;
}
#ifdef E57_DEBUG
void BitpackEncoder::dump( int indent, std::ostream &os ) const
{
Encoder::dump( indent, os );
os << space( indent ) << "sourceBuffer:" << std::endl;
sourceBuffer_->dump( indent + 4, os );
os << space( indent ) << "outBuffer.size: " << outBuffer_.size() << std::endl;
os << space( indent ) << "outBufferFirst: " << outBufferFirst_ << std::endl;
os << space( indent ) << "outBufferEnd: " << outBufferEnd_ << std::endl;
os << space( indent ) << "outBufferAlignmentSize: " << outBufferAlignmentSize_ << std::endl;
os << space( indent ) << "currentRecordIndex: " << currentRecordIndex_ << std::endl;
os << space( indent ) << "outBuffer:" << std::endl;
unsigned i;
for ( i = 0; i < outBuffer_.size() && i < 20; i++ )
{
os << space( indent + 4 ) << "outBuffer[" << i
<< "]: " << static_cast<unsigned>( static_cast<unsigned char>( outBuffer_.at( i ) ) ) << std::endl;
}
if ( i < outBuffer_.size() )
{
os << space( indent + 4 ) << outBuffer_.size() - i << " more unprinted..." << std::endl;
}
}
#endif
//================
BitpackFloatEncoder::BitpackFloatEncoder( unsigned bytestreamNumber, SourceDestBuffer &sbuf, unsigned outputMaxSize,
FloatPrecision precision ) :
BitpackEncoder( bytestreamNumber, sbuf, outputMaxSize,
( precision == E57_SINGLE ) ? sizeof( float ) : sizeof( double ) ),
precision_( precision )
{
}
uint64_t BitpackFloatEncoder::processRecords( size_t recordCount )
{
#ifdef E57_MAX_VERBOSE
std::cout << " BitpackFloatEncoder::processRecords() called, recordCount=" << recordCount << std::endl; //???
#endif
/// Before we add any more, try to shift current contents of outBuffer_ down
/// to beginning of buffer. This leaves outBufferEnd_ at a natural boundary.
outBufferShiftDown();
size_t typeSize = ( precision_ == E57_SINGLE ) ? sizeof( float ) : sizeof( double );
#ifdef E57_DEBUG
/// Verify that outBufferEnd_ is multiple of typeSize (so transfers of floats
/// are aligned naturally in memory).
if ( outBufferEnd_ % typeSize )
throw E57_EXCEPTION2( E57_ERROR_INTERNAL,
"outBufferEnd=" + toString( outBufferEnd_ ) + " typeSize=" + toString( typeSize ) );
#endif
/// Figure out how many records will fit in output.
size_t maxOutputRecords = ( outBuffer_.size() - outBufferEnd_ ) / typeSize;
/// Can't process more records than will safely fit in output stream
if ( recordCount > maxOutputRecords )
{
recordCount = maxOutputRecords;
}
if ( precision_ == E57_SINGLE )
{
/// Form the starting address for next available location in outBuffer
auto outp = reinterpret_cast<float *>( &outBuffer_[outBufferEnd_] );
/// Copy floats from sourceBuffer_ to outBuffer_
for ( unsigned i = 0; i < recordCount; i++ )
{
outp[i] = sourceBuffer_->getNextFloat();
#ifdef E57_MAX_VERBOSE
std::cout << "encoding float: " << outp[i] << std::endl;
#endif
}
}
else
{ /// E57_DOUBLE precision
/// Form the starting address for next available location in outBuffer
auto outp = reinterpret_cast<double *>( &outBuffer_[outBufferEnd_] );
/// Copy doubles from sourceBuffer_ to outBuffer_
for ( unsigned i = 0; i < recordCount; i++ )
{
outp[i] = sourceBuffer_->getNextDouble();
#ifdef E57_MAX_VERBOSE
std::cout << "encoding double: " << outp[i] << std::endl;
#endif
}
}
/// Update end of outBuffer
outBufferEnd_ += recordCount * typeSize;
/// Update counts of records processed
currentRecordIndex_ += recordCount;
return ( currentRecordIndex_ );
}
bool BitpackFloatEncoder::registerFlushToOutput()
{
/// Since have no registers in encoder, return success
return ( true );
}
float BitpackFloatEncoder::bitsPerRecord()
{
return ( ( precision_ == E57_SINGLE ) ? 32.0F : 64.0F );
}
#ifdef E57_DEBUG
void BitpackFloatEncoder::dump( int indent, std::ostream &os ) const
{
BitpackEncoder::dump( indent, os );
if ( precision_ == E57_SINGLE )
{
os << space( indent ) << "precision: E57_SINGLE" << std::endl;
}
else
{
os << space( indent ) << "precision: E57_DOUBLE" << std::endl;
}
}
#endif
//================
BitpackStringEncoder::BitpackStringEncoder( unsigned bytestreamNumber, SourceDestBuffer &sbuf,
unsigned outputMaxSize ) :
BitpackEncoder( bytestreamNumber, sbuf, outputMaxSize, 1 ),
totalBytesProcessed_( 0 ), isStringActive_( false ), prefixComplete_( false ), currentCharPosition_( 0 )
{
}
uint64_t BitpackStringEncoder::processRecords( size_t recordCount )
{
#ifdef E57_MAX_VERBOSE
std::cout << " BitpackStringEncoder::processRecords() called, recordCount=" << recordCount << std::endl; //???
#endif
/// Before we add any more, try to shift current contents of outBuffer_ down
/// to beginning of buffer.
outBufferShiftDown();
/// Figure out how many bytes outBuffer can accept.
size_t bytesFree = outBuffer_.size() - outBufferEnd_;
/// Form the starting address for next available location in outBuffer
char *outp = &outBuffer_[outBufferEnd_];
unsigned recordsProcessed = 0;
/// Don't start loop unless have at least 8 bytes for worst case string
/// length prefix
while ( recordsProcessed < recordCount && bytesFree >= 8 )
{ //??? should be able to proceed if only 1 byte free
if ( isStringActive_ && !prefixComplete_ )
{
/// Calc the length prefix, either 1 byte or 8 bytes
size_t len = currentString_.length();
if ( len <= 127 )
{
#ifdef E57_MAX_VERBOSE
std::cout << "encoding short string: (len=" << len
<< ") "
""
<< currentString_
<< ""
""
<< std::endl;
#endif
/// We can use the short length prefix: b0=0, b7-b1=len
auto lengthPrefix = static_cast<uint8_t>( len << 1 );
*outp++ = lengthPrefix;
bytesFree--;
}
else
{
#ifdef E57_DEBUG
/// Double check have space
if ( bytesFree < 8 )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "bytesFree=" + toString( bytesFree ) );
}
#endif
#ifdef E57_MAX_VERBOSE
std::cout << "encoding long string: (len=" << len
<< ") "
""
<< currentString_
<< ""
""
<< std::endl;
#endif
/// We use the long length prefix: b0=1, b63-b1=len, and store in
/// little endian order Shift the length and set the least
/// significant bit, b0=1.
uint64_t lengthPrefix = ( static_cast<uint64_t>( len ) << 1 ) | 1LL;
*outp++ = static_cast<uint8_t>( lengthPrefix );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 1 * 8 ) );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 2 * 8 ) );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 3 * 8 ) );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 4 * 8 ) );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 5 * 8 ) );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 6 * 8 ) );
*outp++ = static_cast<uint8_t>( lengthPrefix >> ( 7 * 8 ) );
bytesFree -= 8;
}
prefixComplete_ = true;
currentCharPosition_ = 0;
}
if ( isStringActive_ )
{
/// Copy as much string as will fit in outBuffer
size_t bytesToProcess = std::min( currentString_.length() - currentCharPosition_, bytesFree );
for ( size_t i = 0; i < bytesToProcess; i++ )
{
*outp++ = currentString_[currentCharPosition_ + i];
}
currentCharPosition_ += bytesToProcess;
totalBytesProcessed_ += bytesToProcess;
bytesFree -= bytesToProcess;
/// Check if finished string
if ( currentCharPosition_ == currentString_.length() )
{
isStringActive_ = false;
recordsProcessed++;
}
}
if ( !isStringActive_ && recordsProcessed < recordCount )
{
/// Get next string from sourceBuffer
currentString_ = sourceBuffer_->getNextString();
isStringActive_ = true;
prefixComplete_ = false;
currentCharPosition_ = 0;
#ifdef E57_MAX_VERBOSE
std::cout << "getting next string, length=" << currentString_.length() << std::endl;
#endif
}
}
/// Update end of outBuffer
outBufferEnd_ = outBuffer_.size() - bytesFree;
/// Update counts of records processed
currentRecordIndex_ += recordsProcessed;
return ( currentRecordIndex_ );
}
bool BitpackStringEncoder::registerFlushToOutput()
{
/// Since have no registers in encoder, return success
return ( true );
}
float BitpackStringEncoder::bitsPerRecord()
{
/// Return average number of bits in strings + 8 bits for prefix
if ( currentRecordIndex_ > 0 )
{
return ( 8.0f * totalBytesProcessed_ ) / currentRecordIndex_ + 8;
}
/// We haven't completed a record yet, so guess 100 bytes per record
return 100 * 8.0f;
}
#ifdef E57_DEBUG
void BitpackStringEncoder::dump( int indent, std::ostream &os ) const
{
BitpackEncoder::dump( indent, os );
os << space( indent ) << "totalBytesProcessed: " << totalBytesProcessed_ << std::endl;
os << space( indent ) << "isStringActive: " << isStringActive_ << std::endl;
os << space( indent ) << "prefixComplete: " << prefixComplete_ << std::endl;
os << space( indent ) << "currentString: " << currentString_ << std::endl;
os << space( indent ) << "currentCharPosition: " << currentCharPosition_ << std::endl;
}
#endif
//================================================================
template <typename RegisterT>
BitpackIntegerEncoder<RegisterT>::BitpackIntegerEncoder( bool isScaledInteger, unsigned bytestreamNumber,
SourceDestBuffer &sbuf, unsigned outputMaxSize,
int64_t minimum, int64_t maximum, double scale,
double offset ) :
BitpackEncoder( bytestreamNumber, sbuf, outputMaxSize, sizeof( RegisterT ) )
{
/// Get pointer to parent ImageFileImpl
ImageFileImplSharedPtr imf( sbuf.impl()->destImageFile() ); //??? should be function for this,
// imf->parentFile() --> ImageFile?
isScaledInteger_ = isScaledInteger;
minimum_ = minimum;
maximum_ = maximum;
scale_ = scale;
offset_ = offset;
bitsPerRecord_ = imf->bitsNeeded( minimum_, maximum_ );
sourceBitMask_ = ( bitsPerRecord_ == 64 ) ? ~0 : ( 1ULL << bitsPerRecord_ ) - 1;
registerBitsUsed_ = 0;
register_ = 0;
}
template <typename RegisterT> uint64_t BitpackIntegerEncoder<RegisterT>::processRecords( size_t recordCount )
{
//??? what are state guarantees if get an exception during transfer?
#ifdef E57_MAX_VERBOSE
std::cout << "BitpackIntegerEncoder::processRecords() called, sizeof(RegisterT)=" << sizeof( RegisterT )
<< " recordCount=" << recordCount << std::endl;
dump( 4 );
#endif
#ifdef E57_MAX_DEBUG
/// Double check that register will hold at least one input records worth of
/// bits
if ( 8 * sizeof( RegisterT ) < bitsPerRecord_ )
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "bitsPerRecord=" + toString( bitsPerRecord_ ) );
#endif
/// Before we add any more, try to shift current contents of outBuffer_ down
/// to beginning of buffer. This leaves outBufferEnd_ at a natural boundary.
outBufferShiftDown();
#ifdef E57_DEBUG
/// Verify that outBufferEnd_ is multiple of sizeof(RegisterT) (so transfers
/// of RegisterT are aligned naturally in memory).
if ( outBufferEnd_ % sizeof( RegisterT ) )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "outBufferEnd=" + toString( outBufferEnd_ ) );
}
size_t transferMax = ( outBuffer_.size() - outBufferEnd_ ) / sizeof( RegisterT );
#endif
/// Precalculate exact maximum number of records that will fit in output
/// before overflow.
size_t outputWordCapacity = ( outBuffer_.size() - outBufferEnd_ ) / sizeof( RegisterT );
size_t maxOutputRecords =
( outputWordCapacity * 8 * sizeof( RegisterT ) + 8 * sizeof( RegisterT ) - registerBitsUsed_ - 1 ) /
bitsPerRecord_;
/// Number of transfers is the smaller of what was requested and what will
/// fit.
recordCount = std::min( recordCount, maxOutputRecords );
#ifdef E57_MAX_VERBOSE
std::cout << " outputWordCapacity=" << outputWordCapacity << " maxOutputRecords=" << maxOutputRecords
<< " recordCount=" << recordCount << std::endl;
#endif
/// Form the starting address for next available location in outBuffer
auto outp = reinterpret_cast<RegisterT *>( &outBuffer_[outBufferEnd_] );
unsigned outTransferred = 0;
/// Copy bits from sourceBuffer_ to outBuffer_
for ( unsigned i = 0; i < recordCount; i++ )
{
int64_t rawValue;
/// The parameter isScaledInteger_ determines which version of
/// getNextInt64 gets called
if ( isScaledInteger_ )
{
rawValue = sourceBuffer_->getNextInt64( scale_, offset_ );
}
else
rawValue = sourceBuffer_->getNextInt64();
/// Enforce min/max specification on value
if ( rawValue < minimum_ || maximum_ < rawValue )
{
throw E57_EXCEPTION2( E57_ERROR_VALUE_OUT_OF_BOUNDS, "rawValue=" + toString( rawValue ) +
" minimum=" + toString( minimum_ ) +
" maximum=" + toString( maximum_ ) );
}
auto uValue = static_cast<uint64_t>( rawValue - minimum_ );
#ifdef E57_MAX_VERBOSE
std::cout << "encoding integer rawValue=" << binaryString( rawValue ) << " = " << hexString( rawValue )
<< std::endl;
std::cout << " uValue =" << binaryString( uValue ) << " = " << hexString( uValue ) << std::endl;
#endif
#ifdef E57_DEBUG
/// Double check that no bits outside of the mask are set
if ( uValue & ~static_cast<uint64_t>( sourceBitMask_ ) )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "uValue=" + toString( uValue ) );
}
#endif
/// Mask off upper bits (just in case)
uValue &= static_cast<uint64_t>( sourceBitMask_ );
/// See if uValue bits will fit in register
unsigned newRegisterBitsUsed = registerBitsUsed_ + bitsPerRecord_;
#ifdef E57_MAX_VERBOSE
std::cout << " registerBitsUsed=" << registerBitsUsed_ << " newRegisterBitsUsed=" << newRegisterBitsUsed
<< std::endl;
#endif
if ( newRegisterBitsUsed > 8 * sizeof( RegisterT ) )
{
/// Have more than one registers worth, fill register, transfer, then
/// fill some more
register_ |= static_cast<RegisterT>( uValue ) << registerBitsUsed_;
#ifdef E57_DEBUG
/// Before transfer, double check address within bounds
if ( outTransferred >= transferMax )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "outTransferred=" + toString( outTransferred ) + " transferMax" +
toString( transferMax ) );
}
#endif
outp[outTransferred] = register_;
outTransferred++;
register_ = static_cast<RegisterT>( uValue ) >> ( 8 * sizeof( RegisterT ) - registerBitsUsed_ );
registerBitsUsed_ = newRegisterBitsUsed - 8 * sizeof( RegisterT );
}
else if ( newRegisterBitsUsed == 8 * sizeof( RegisterT ) )
{
/// Input will exactly fill register, insert value, then transfer
register_ |= static_cast<RegisterT>( uValue ) << registerBitsUsed_;
#ifdef E57_DEBUG
/// Before transfer, double check address within bounds
if ( outTransferred >= transferMax )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "outTransferred=" + toString( outTransferred ) + " transferMax" +
toString( transferMax ) );
}
#endif
outp[outTransferred] = register_;
outTransferred++;
register_ = 0;
registerBitsUsed_ = 0;
}
else
{
/// There is extra room in register, insert value, but don't do
/// transfer yet
register_ |= static_cast<RegisterT>( uValue ) << registerBitsUsed_;
registerBitsUsed_ = newRegisterBitsUsed;
}
#ifdef E57_MAX_VERBOSE
std::cout << " After " << outTransferred << " transfers and " << i + 1 << " records, encoder:" << std::endl;
dump( 4 );
#endif
}
/// Update tail of output buffer
outBufferEnd_ += outTransferred * sizeof( RegisterT );
#ifdef E57_DEBUG
/// Double check end is ok
if ( outBufferEnd_ > outBuffer_.size() )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "outBufferEnd=" + toString( outBufferEnd_ ) +
" outBuffersize=" + toString( outBuffer_.size() ) );
}
#endif
/// Update counts of records processed
currentRecordIndex_ += recordCount;
return ( currentRecordIndex_ );
}
template <typename RegisterT> bool BitpackIntegerEncoder<RegisterT>::registerFlushToOutput()
{
#ifdef E57_MAX_VERBOSE
std::cout << "BitpackIntegerEncoder::registerFlushToOutput() called, "
"sizeof(RegisterT)="
<< sizeof( RegisterT ) << std::endl;
dump( 4 );
#endif
/// If have any used bits in register, transfer to output, padded in MSBits
/// with zeros to RegisterT boundary
if ( registerBitsUsed_ > 0 )
{
if ( outBufferEnd_ < outBuffer_.size() - sizeof( RegisterT ) )
{
auto outp = reinterpret_cast<RegisterT *>( &outBuffer_[outBufferEnd_] );
*outp = register_;
register_ = 0;
registerBitsUsed_ = 0;
outBufferEnd_ += sizeof( RegisterT );
return true; // flush succeeded ??? is this used? correctly?
}
return false; // flush didn't complete (not enough room).
}
return true;
}
template <typename RegisterT> float BitpackIntegerEncoder<RegisterT>::bitsPerRecord()
{
return ( static_cast<float>( bitsPerRecord_ ) );
}
#ifdef E57_DEBUG
template <typename RegisterT> void BitpackIntegerEncoder<RegisterT>::dump( int indent, std::ostream &os ) const
{
BitpackEncoder::dump( indent, os );
os << space( indent ) << "isScaledInteger: " << isScaledInteger_ << std::endl;
os << space( indent ) << "minimum: " << minimum_ << std::endl;
os << space( indent ) << "maximum: " << maximum_ << std::endl;
os << space( indent ) << "scale: " << scale_ << std::endl;
os << space( indent ) << "offset: " << offset_ << std::endl;
os << space( indent ) << "bitsPerRecord: " << bitsPerRecord_ << std::endl;
os << space( indent ) << "sourceBitMask: " << binaryString( sourceBitMask_ ) << " " << hexString( sourceBitMask_ )
<< std::endl;
os << space( indent ) << "register: " << binaryString( register_ ) << " " << hexString( register_ )
<< std::endl;
os << space( indent ) << "registerBitsUsed: " << registerBitsUsed_ << std::endl;
}
#endif
//================================================================
ConstantIntegerEncoder::ConstantIntegerEncoder( unsigned bytestreamNumber, SourceDestBuffer &sbuf, int64_t minimum ) :
Encoder( bytestreamNumber ), sourceBuffer_( sbuf.impl() ), currentRecordIndex_( 0 ), minimum_( minimum )
{
}
uint64_t ConstantIntegerEncoder::processRecords( size_t recordCount )
{
#ifdef E57_MAX_VERBOSE
std::cout << "ConstantIntegerEncoder::processRecords() called, recordCount=" << recordCount << std::endl;
dump( 4 );
#endif
/// Check that all source values are == minimum_
for ( unsigned i = 0; i < recordCount; i++ )
{
int64_t nextInt64 = sourceBuffer_->getNextInt64();
if ( nextInt64 != minimum_ )
{
throw E57_EXCEPTION2( E57_ERROR_VALUE_OUT_OF_BOUNDS,
"nextInt64=" + toString( nextInt64 ) + " minimum=" + toString( minimum_ ) );
}
}
/// Update counts of records processed
currentRecordIndex_ += recordCount;
return ( currentRecordIndex_ );
}
unsigned ConstantIntegerEncoder::sourceBufferNextIndex()
{
return ( sourceBuffer_->nextIndex() );
}
uint64_t ConstantIntegerEncoder::currentRecordIndex()
{
return ( currentRecordIndex_ );
}
float ConstantIntegerEncoder::bitsPerRecord()
{
/// We don't produce any output
return ( 0.0 );
}
bool ConstantIntegerEncoder::registerFlushToOutput()
{
return ( true );
}
size_t ConstantIntegerEncoder::outputAvailable() const
{
/// We don't produce any output
return 0;
}
void ConstantIntegerEncoder::outputRead( char * /*dest*/, const size_t byteCount )
{
/// Should never request any output data
if ( byteCount > 0 )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "byteCount=" + toString( byteCount ) );
}
}
void ConstantIntegerEncoder::outputClear()
{
}
void ConstantIntegerEncoder::sourceBufferSetNew( std::vector<SourceDestBuffer> &sbufs )
{
/// Verify that this encoder only has single input buffer
if ( sbufs.size() != 1 )
{
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "sbufsSize=" + toString( sbufs.size() ) );
}
sourceBuffer_ = sbufs.at( 0 ).impl();
}
size_t ConstantIntegerEncoder::outputGetMaxSize()
{
/// We don't produce any output
return ( 0 );
}
void ConstantIntegerEncoder::outputSetMaxSize( unsigned /*byteCount*/ )
{
/// Ignore, since don't produce any output
}
#ifdef E57_DEBUG
void ConstantIntegerEncoder::dump( int indent, std::ostream &os ) const
{
Encoder::dump( indent, os );
os << space( indent ) << "currentRecordIndex: " << currentRecordIndex_ << std::endl;
os << space( indent ) << "minimum: " << minimum_ << std::endl;
os << space( indent ) << "sourceBuffer:" << std::endl;
sourceBuffer_->dump( indent + 4, os );
}
#endif