893 lines
35 KiB
C++
893 lines
35 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 "Decoder.h"
|
|
#include "FloatNodeImpl.h"
|
|
#include "ImageFileImpl.h"
|
|
#include "IntegerNodeImpl.h"
|
|
#include "ScaledIntegerNodeImpl.h"
|
|
#include "SourceDestBufferImpl.h"
|
|
|
|
using namespace e57;
|
|
|
|
std::shared_ptr<Decoder> Decoder::DecoderFactory( unsigned bytestreamNumber, //!!! name ok?
|
|
const CompressedVectorNodeImpl *cVector,
|
|
std::vector<SourceDestBuffer> &dbufs, const ustring & /*codecPath*/ )
|
|
{
|
|
//!!! verify single dbuf
|
|
|
|
/// Get node we are going to decode from the CompressedVector's prototype
|
|
NodeImplSharedPtr prototype = cVector->getPrototype();
|
|
ustring path = dbufs.at( 0 ).pathName();
|
|
NodeImplSharedPtr decodeNode = prototype->get( path );
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "Node to decode:" << std::endl; //???
|
|
decodeNode->dump( 2 );
|
|
#endif
|
|
|
|
uint64_t maxRecordCount = cVector->childCount();
|
|
|
|
switch ( decodeNode->type() )
|
|
{
|
|
case E57_INTEGER:
|
|
{
|
|
std::shared_ptr<IntegerNodeImpl> ini =
|
|
std::static_pointer_cast<IntegerNodeImpl>( decodeNode ); // downcast to correct type
|
|
|
|
/// Get pointer to parent ImageFileImpl, to call bitsNeeded()
|
|
ImageFileImplSharedPtr imf( decodeNode->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 decoder with appropriate register size, based on
|
|
/// number of bits stored.
|
|
if ( bitsPerRecord == 0 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder( new ConstantIntegerDecoder( false, bytestreamNumber, dbufs.at( 0 ),
|
|
ini->minimum(), 1.0, 0.0, maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
if ( bitsPerRecord <= 8 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder( new BitpackIntegerDecoder<uint8_t>(
|
|
false, bytestreamNumber, dbufs.at( 0 ), ini->minimum(), ini->maximum(), 1.0, 0.0, maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
if ( bitsPerRecord <= 16 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder( new BitpackIntegerDecoder<uint16_t>(
|
|
false, bytestreamNumber, dbufs.at( 0 ), ini->minimum(), ini->maximum(), 1.0, 0.0, maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
if ( bitsPerRecord <= 32 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder( new BitpackIntegerDecoder<uint32_t>(
|
|
false, bytestreamNumber, dbufs.at( 0 ), ini->minimum(), ini->maximum(), 1.0, 0.0, maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
std::shared_ptr<Decoder> decoder( new BitpackIntegerDecoder<uint64_t>(
|
|
false, bytestreamNumber, dbufs.at( 0 ), ini->minimum(), ini->maximum(), 1.0, 0.0, maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
case E57_SCALED_INTEGER:
|
|
{
|
|
std::shared_ptr<ScaledIntegerNodeImpl> sini =
|
|
std::static_pointer_cast<ScaledIntegerNodeImpl>( decodeNode ); // downcast to correct type
|
|
|
|
/// Get pointer to parent ImageFileImpl, to call bitsNeeded()
|
|
ImageFileImplSharedPtr imf( decodeNode->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
|
|
/// Construct ScaledInteger dencoder with appropriate register size,
|
|
/// based on number of bits stored.
|
|
if ( bitsPerRecord == 0 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder( new ConstantIntegerDecoder( true, bytestreamNumber, dbufs.at( 0 ),
|
|
sini->minimum(), sini->scale(),
|
|
sini->offset(), maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
if ( bitsPerRecord <= 8 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder(
|
|
new BitpackIntegerDecoder<uint8_t>( true, bytestreamNumber, dbufs.at( 0 ), sini->minimum(),
|
|
sini->maximum(), sini->scale(), sini->offset(), maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
if ( bitsPerRecord <= 16 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder(
|
|
new BitpackIntegerDecoder<uint16_t>( true, bytestreamNumber, dbufs.at( 0 ), sini->minimum(),
|
|
sini->maximum(), sini->scale(), sini->offset(), maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
if ( bitsPerRecord <= 32 )
|
|
{
|
|
std::shared_ptr<Decoder> decoder(
|
|
new BitpackIntegerDecoder<uint32_t>( true, bytestreamNumber, dbufs.at( 0 ), sini->minimum(),
|
|
sini->maximum(), sini->scale(), sini->offset(), maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
std::shared_ptr<Decoder> decoder(
|
|
new BitpackIntegerDecoder<uint64_t>( true, bytestreamNumber, dbufs.at( 0 ), sini->minimum(),
|
|
sini->maximum(), sini->scale(), sini->offset(), maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
case E57_FLOAT:
|
|
{
|
|
std::shared_ptr<FloatNodeImpl> fni =
|
|
std::static_pointer_cast<FloatNodeImpl>( decodeNode ); // downcast to correct type
|
|
|
|
std::shared_ptr<Decoder> decoder(
|
|
new BitpackFloatDecoder( bytestreamNumber, dbufs.at( 0 ), fni->precision(), maxRecordCount ) );
|
|
return decoder;
|
|
}
|
|
|
|
case E57_STRING:
|
|
{
|
|
std::shared_ptr<Decoder> decoder(
|
|
new BitpackStringDecoder( bytestreamNumber, dbufs.at( 0 ), maxRecordCount ) );
|
|
|
|
return decoder;
|
|
}
|
|
|
|
default:
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_BAD_PROTOTYPE, "nodeType=" + toString( decodeNode->type() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
Decoder::Decoder( unsigned bytestreamNumber ) : bytestreamNumber_( bytestreamNumber )
|
|
{
|
|
}
|
|
|
|
BitpackDecoder::BitpackDecoder( unsigned bytestreamNumber, SourceDestBuffer &dbuf, unsigned alignmentSize,
|
|
uint64_t maxRecordCount ) :
|
|
Decoder( bytestreamNumber ),
|
|
maxRecordCount_( maxRecordCount ), destBuffer_( dbuf.impl() ),
|
|
inBuffer_( 1024 ), //!!! need to pick smarter channel buffer sizes
|
|
inBufferAlignmentSize_( alignmentSize ), bitsPerWord_( 8 * alignmentSize ), bytesPerWord_( alignmentSize )
|
|
{
|
|
}
|
|
|
|
void BitpackDecoder::destBufferSetNew( std::vector<SourceDestBuffer> &dbufs )
|
|
{
|
|
if ( dbufs.size() != 1 )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "dbufsSize=" + toString( dbufs.size() ) );
|
|
}
|
|
|
|
destBuffer_ = dbufs.at( 0 ).impl();
|
|
}
|
|
|
|
size_t BitpackDecoder::inputProcess( const char *source, const size_t availableByteCount )
|
|
{
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "BitpackDecoder::inputprocess() called, source=" << ( source ? source : "none" )
|
|
<< " availableByteCount=" << availableByteCount << std::endl;
|
|
#endif
|
|
size_t bytesUnsaved = availableByteCount;
|
|
size_t bitsEaten = 0;
|
|
do
|
|
{
|
|
size_t byteCount = std::min( bytesUnsaved, inBuffer_.size() - static_cast<size_t>( inBufferEndByte_ ) );
|
|
|
|
/// Copy input bytes from caller, if any
|
|
if ( ( byteCount > 0 ) && source )
|
|
{
|
|
memcpy( &inBuffer_[inBufferEndByte_], source, byteCount );
|
|
|
|
/// Advance tail pointer.
|
|
inBufferEndByte_ += byteCount;
|
|
|
|
/// Update amount available from caller
|
|
bytesUnsaved -= byteCount;
|
|
source += byteCount;
|
|
}
|
|
#ifdef E57_MAX_VERBOSE
|
|
{
|
|
unsigned i;
|
|
unsigned firstByte = inBufferFirstBit_ / 8;
|
|
for ( i = 0; i < byteCount && i < 20; i++ )
|
|
{
|
|
std::cout << " inBuffer[" << firstByte + i << "]=" << (unsigned)(unsigned char)( inBuffer_[firstByte + i] )
|
|
<< std::endl;
|
|
}
|
|
if ( i < byteCount )
|
|
{
|
|
std::cout << " " << byteCount - i << "source bytes unprinted..." << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// ??? fix doc for new bit interface
|
|
/// Now that we have input stored in an aligned buffer, call derived class
|
|
/// to try to eat some Note that end of filled buffer may not be at a
|
|
/// natural boundary. The subclass may transfer this partial word in a
|
|
/// full word transfer, but it must be done carefully to only use the defined
|
|
/// bits. inBuffer_ is a multiple of largest word size, so this full word
|
|
/// transfer off the end will always be in defined memory.
|
|
|
|
size_t firstWord = inBufferFirstBit_ / bitsPerWord_;
|
|
size_t firstNaturalBit = firstWord * bitsPerWord_;
|
|
size_t endBit = inBufferEndByte_ * 8;
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " feeding aligned decoder " << endBit - inBufferFirstBit_ << " bits." << std::endl;
|
|
#endif
|
|
bitsEaten = inputProcessAligned( &inBuffer_[firstWord * bytesPerWord_], inBufferFirstBit_ - firstNaturalBit,
|
|
endBit - firstNaturalBit );
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " bitsEaten=" << bitsEaten << " firstWord=" << firstWord << " firstNaturalBit=" << firstNaturalBit
|
|
<< " endBit=" << endBit << std::endl;
|
|
#endif
|
|
#ifdef E57_DEBUG
|
|
if ( bitsEaten > endBit - inBufferFirstBit_ )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "bitsEaten=" + toString( bitsEaten ) +
|
|
" endBit=" + toString( endBit ) +
|
|
" inBufferFirstBit=" + toString( inBufferFirstBit_ ) );
|
|
}
|
|
#endif
|
|
inBufferFirstBit_ += bitsEaten;
|
|
|
|
/// Shift uneaten data to beginning of inBuffer_, keep on natural word
|
|
/// boundaries.
|
|
inBufferShiftDown();
|
|
|
|
/// If the lower level processing didn't eat anything on this iteration,
|
|
/// stop looping and tell caller how much we ate or stored.
|
|
} while ( bytesUnsaved > 0 && bitsEaten > 0 );
|
|
|
|
/// Return the number of bytes we ate/saved.
|
|
return ( availableByteCount - bytesUnsaved );
|
|
}
|
|
|
|
void BitpackDecoder::stateReset()
|
|
{
|
|
inBufferFirstBit_ = 0;
|
|
inBufferEndByte_ = 0;
|
|
}
|
|
|
|
void BitpackDecoder::inBufferShiftDown()
|
|
{
|
|
/// Move uneaten data down to beginning of inBuffer_.
|
|
/// Keep on natural boundaries.
|
|
/// Moves all of word that contains inBufferFirstBit.
|
|
size_t firstWord = inBufferFirstBit_ / bitsPerWord_;
|
|
size_t firstNaturalByte = firstWord * bytesPerWord_;
|
|
#ifdef E57_DEBUG
|
|
if ( firstNaturalByte > inBufferEndByte_ )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "firstNaturalByte=" + toString( firstNaturalByte ) +
|
|
" inBufferEndByte=" + toString( inBufferEndByte_ ) );
|
|
}
|
|
#endif
|
|
size_t byteCount = inBufferEndByte_ - firstNaturalByte;
|
|
if ( byteCount > 0 )
|
|
{
|
|
memmove( &inBuffer_[0], &inBuffer_[firstNaturalByte],
|
|
byteCount ); /// Overlapping regions ok with memmove().
|
|
}
|
|
|
|
/// Update indexes
|
|
inBufferEndByte_ = byteCount;
|
|
inBufferFirstBit_ = inBufferFirstBit_ % bitsPerWord_;
|
|
}
|
|
|
|
#ifdef E57_DEBUG
|
|
void BitpackDecoder::dump( int indent, std::ostream &os )
|
|
{
|
|
os << space( indent ) << "bytestreamNumber: " << bytestreamNumber_ << std::endl;
|
|
os << space( indent ) << "currentRecordIndex: " << currentRecordIndex_ << std::endl;
|
|
os << space( indent ) << "maxRecordCount: " << maxRecordCount_ << std::endl;
|
|
os << space( indent ) << "destBuffer:" << std::endl;
|
|
destBuffer_->dump( indent + 4, os );
|
|
os << space( indent ) << "inBufferFirstBit: " << inBufferFirstBit_ << std::endl;
|
|
os << space( indent ) << "inBufferEndByte: " << inBufferEndByte_ << std::endl;
|
|
os << space( indent ) << "inBufferAlignmentSize: " << inBufferAlignmentSize_ << std::endl;
|
|
os << space( indent ) << "bitsPerWord: " << bitsPerWord_ << std::endl;
|
|
os << space( indent ) << "bytesPerWord: " << bytesPerWord_ << std::endl;
|
|
os << space( indent ) << "inBuffer:" << std::endl;
|
|
unsigned i;
|
|
for ( i = 0; i < inBuffer_.size() && i < 20; i++ )
|
|
{
|
|
os << space( indent + 4 ) << "inBuffer[" << i
|
|
<< "]: " << static_cast<unsigned>( static_cast<unsigned char>( inBuffer_.at( i ) ) ) << std::endl;
|
|
}
|
|
if ( i < inBuffer_.size() )
|
|
{
|
|
os << space( indent + 4 ) << inBuffer_.size() - i << " more unprinted..." << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//================================================================
|
|
|
|
BitpackFloatDecoder::BitpackFloatDecoder( unsigned bytestreamNumber, SourceDestBuffer &dbuf, FloatPrecision precision,
|
|
uint64_t maxRecordCount ) :
|
|
BitpackDecoder( bytestreamNumber, dbuf, ( precision == E57_SINGLE ) ? sizeof( float ) : sizeof( double ),
|
|
maxRecordCount ),
|
|
precision_( precision )
|
|
{
|
|
}
|
|
|
|
size_t BitpackFloatDecoder::inputProcessAligned( const char *inbuf, const size_t firstBit, const size_t endBit )
|
|
{
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "BitpackFloatDecoder::inputProcessAligned() called, inbuf=" << inbuf << " firstBit=" << firstBit
|
|
<< " endBit=" << endBit << std::endl;
|
|
#endif
|
|
/// Read from inbuf, decode, store in destBuffer
|
|
/// Repeat until have filled destBuffer, or completed all records
|
|
|
|
size_t n = destBuffer_->capacity() - destBuffer_->nextIndex();
|
|
|
|
size_t typeSize = ( precision_ == E57_SINGLE ) ? sizeof( float ) : sizeof( double );
|
|
|
|
#ifdef E57_DEBUG
|
|
#if 0 // I know no way to do this portably <rs>
|
|
// Deactivate for now until a better solution is found.
|
|
/// Verify that inbuf is naturally aligned to correct boundary (4 or 8 bytes). Base class should be doing this for us.
|
|
if (reinterpret_cast<unsigned>(inbuf) % typeSize) {
|
|
throw E57_EXCEPTION2(E57_ERROR_INTERNAL,
|
|
"inbuf=" + toString(reinterpret_cast<unsigned>(inbuf))
|
|
+ " typeSize=" + toString(typeSize));
|
|
}
|
|
#endif
|
|
/// Verify first bit is zero
|
|
if ( firstBit != 0 )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "firstBit=" + toString( firstBit ) );
|
|
}
|
|
#endif
|
|
|
|
/// Calc how many whole records worth of data we have in inbuf
|
|
size_t maxInputRecords = ( endBit - firstBit ) / ( 8 * typeSize );
|
|
|
|
/// Can't process more records than we have input data for.
|
|
if ( n > maxInputRecords )
|
|
{
|
|
n = maxInputRecords;
|
|
}
|
|
|
|
// Can't process more than defined in input file
|
|
if ( n > maxRecordCount_ - currentRecordIndex_ )
|
|
{
|
|
n = static_cast<unsigned>( maxRecordCount_ - currentRecordIndex_ );
|
|
}
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " n:" << n << std::endl; //???
|
|
#endif
|
|
|
|
if ( precision_ == E57_SINGLE )
|
|
{
|
|
/// Form the starting address for first data location in inBuffer
|
|
auto inp = reinterpret_cast<const float *>( inbuf );
|
|
|
|
/// Copy floats from inbuf to destBuffer_
|
|
for ( unsigned i = 0; i < n; i++ )
|
|
{
|
|
float value = *inp;
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " got float value=" << value << std::endl;
|
|
#endif
|
|
destBuffer_->setNextFloat( value );
|
|
inp++;
|
|
}
|
|
}
|
|
else
|
|
{ /// E57_DOUBLE precision
|
|
/// Form the starting address for first data location in inBuffer
|
|
auto inp = reinterpret_cast<const double *>( inbuf );
|
|
|
|
/// Copy doubles from inbuf to destBuffer_
|
|
for ( unsigned i = 0; i < n; i++ )
|
|
{
|
|
double value = *inp;
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " got double value=" << value << std::endl;
|
|
#endif
|
|
destBuffer_->setNextDouble( value );
|
|
inp++;
|
|
}
|
|
}
|
|
|
|
/// Update counts of records processed
|
|
currentRecordIndex_ += n;
|
|
|
|
/// Returned number of bits processed (always a multiple of alignment size).
|
|
return ( n * 8 * typeSize );
|
|
}
|
|
|
|
#ifdef E57_DEBUG
|
|
void BitpackFloatDecoder::dump( int indent, std::ostream &os )
|
|
{
|
|
BitpackDecoder::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
|
|
|
|
//================================================================
|
|
|
|
BitpackStringDecoder::BitpackStringDecoder( unsigned bytestreamNumber, SourceDestBuffer &dbuf,
|
|
uint64_t maxRecordCount ) :
|
|
BitpackDecoder( bytestreamNumber, dbuf, sizeof( char ), maxRecordCount )
|
|
{
|
|
}
|
|
|
|
size_t BitpackStringDecoder::inputProcessAligned( const char *inbuf, const size_t firstBit, const size_t endBit )
|
|
{
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "BitpackStringDecoder::inputProcessAligned() called, inbuf=" << inbuf << " firstBit=" << firstBit
|
|
<< " endBit=" << endBit << std::endl;
|
|
#endif
|
|
/// Read from inbuf, decode, store in destBuffer
|
|
/// Repeat until have filled destBuffer, or completed all records
|
|
|
|
#ifdef E57_DEBUG
|
|
/// Verify first bit is zero (always byte-aligned)
|
|
if ( firstBit != 0 )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "firstBit=" + toString( firstBit ) );
|
|
}
|
|
#endif
|
|
|
|
/// Converts start/end bits to whole bytes
|
|
size_t nBytesAvailable = ( endBit - firstBit ) >> 3;
|
|
size_t nBytesRead = 0;
|
|
|
|
/// Loop until we've finished all the records, or ran out of input currently
|
|
/// available
|
|
while ( currentRecordIndex_ < maxRecordCount_ && nBytesRead < nBytesAvailable )
|
|
{
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "read string loop1: readingPrefix=" << readingPrefix_ << " prefixLength=" << prefixLength_
|
|
<< " nBytesPrefixRead=" << nBytesPrefixRead_ << " nBytesStringRead=" << nBytesStringRead_ << std::endl;
|
|
#endif
|
|
if ( readingPrefix_ )
|
|
{
|
|
/// Try to read more prefix bytes
|
|
while ( nBytesRead < nBytesAvailable && ( nBytesPrefixRead_ == 0 || nBytesPrefixRead_ < prefixLength_ ) )
|
|
{
|
|
/// If first byte of prefix, test the least significant bit to see
|
|
/// how long prefix is
|
|
if ( nBytesPrefixRead_ == 0 )
|
|
{
|
|
if ( *inbuf & 0x01 )
|
|
{
|
|
prefixLength_ = 8; // 8 byte prefix, length up to 2^63-1
|
|
}
|
|
else
|
|
{
|
|
prefixLength_ = 1; // 1 byte prefix, length up to 2^7-1
|
|
}
|
|
}
|
|
|
|
/// Accumulate prefix bytes
|
|
prefixBytes_[nBytesPrefixRead_] = *inbuf++;
|
|
nBytesPrefixRead_++;
|
|
nBytesRead++;
|
|
}
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "read string loop2: readingPrefix=" << readingPrefix_ << " prefixLength=" << prefixLength_
|
|
<< " nBytesPrefixRead=" << nBytesPrefixRead_ << " nBytesStringRead=" << nBytesStringRead_
|
|
<< std::endl;
|
|
#endif
|
|
/// If got all of prefix, convert to length and get ready to read
|
|
/// string
|
|
if ( nBytesPrefixRead_ > 0 && nBytesPrefixRead_ == prefixLength_ )
|
|
{
|
|
if ( prefixLength_ == 1 )
|
|
{
|
|
/// Single byte prefix, extract length from b7-b1.
|
|
/// Removing the least significant bit (which says this is a
|
|
/// short prefix).
|
|
stringLength_ = static_cast<uint64_t>( prefixBytes_[0] >> 1 );
|
|
}
|
|
else
|
|
{
|
|
/// Eight byte prefix, extract length from b63-b1. Little endian
|
|
/// ordering. Removing the least significant bit (which says this
|
|
/// is a long prefix).
|
|
stringLength_ = ( static_cast<uint64_t>( prefixBytes_[0] ) >> 1 ) +
|
|
( static_cast<uint64_t>( prefixBytes_[1] ) << ( 1 * 8 - 1 ) ) +
|
|
( static_cast<uint64_t>( prefixBytes_[2] ) << ( 2 * 8 - 1 ) ) +
|
|
( static_cast<uint64_t>( prefixBytes_[3] ) << ( 3 * 8 - 1 ) ) +
|
|
( static_cast<uint64_t>( prefixBytes_[4] ) << ( 4 * 8 - 1 ) ) +
|
|
( static_cast<uint64_t>( prefixBytes_[5] ) << ( 5 * 8 - 1 ) ) +
|
|
( static_cast<uint64_t>( prefixBytes_[6] ) << ( 6 * 8 - 1 ) ) +
|
|
( static_cast<uint64_t>( prefixBytes_[7] ) << ( 7 * 8 - 1 ) );
|
|
}
|
|
/// Get ready to read string contents
|
|
readingPrefix_ = false;
|
|
prefixLength_ = 1;
|
|
memset( prefixBytes_, 0, sizeof( prefixBytes_ ) );
|
|
nBytesPrefixRead_ = 0;
|
|
currentString_ = "";
|
|
nBytesStringRead_ = 0;
|
|
}
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "read string loop3: readingPrefix=" << readingPrefix_ << " prefixLength=" << prefixLength_
|
|
<< " nBytesPrefixRead=" << nBytesPrefixRead_ << " nBytesStringRead=" << nBytesStringRead_
|
|
<< std::endl;
|
|
#endif
|
|
}
|
|
|
|
/// If currently reading string contents, keep doing it until have
|
|
/// complete string
|
|
if ( !readingPrefix_ )
|
|
{
|
|
/// Calc how many bytes we need to complete current string
|
|
uint64_t nBytesNeeded = stringLength_ - nBytesStringRead_;
|
|
|
|
/// Can process the smaller of unread or needed bytes
|
|
size_t nBytesProcess = nBytesAvailable - nBytesRead;
|
|
if ( nBytesNeeded < static_cast<uint64_t>( nBytesProcess ) )
|
|
{
|
|
nBytesProcess = static_cast<unsigned>( nBytesNeeded );
|
|
}
|
|
|
|
/// Append to current string and update counts
|
|
currentString_ += ustring( inbuf, nBytesProcess );
|
|
inbuf += nBytesProcess;
|
|
nBytesRead += nBytesProcess;
|
|
nBytesStringRead_ += nBytesProcess;
|
|
|
|
/// Check if completed reading the string contents
|
|
if ( nBytesStringRead_ == stringLength_ )
|
|
{
|
|
/// Save accumulated string to dest buffer
|
|
destBuffer_->setNextString( currentString_ );
|
|
currentRecordIndex_++;
|
|
|
|
/// Get ready to read next prefix
|
|
readingPrefix_ = true;
|
|
prefixLength_ = 1;
|
|
memset( prefixBytes_, 0, sizeof( prefixBytes_ ) );
|
|
nBytesPrefixRead_ = 0;
|
|
stringLength_ = 0;
|
|
currentString_ = "";
|
|
nBytesStringRead_ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returned number of bits processed (always a multiple of alignment size).
|
|
return ( nBytesRead * 8 );
|
|
}
|
|
|
|
#ifdef E57_DEBUG
|
|
void BitpackStringDecoder::dump( int indent, std::ostream &os )
|
|
{
|
|
BitpackDecoder::dump( indent, os );
|
|
os << space( indent ) << "readingPrefix: " << readingPrefix_ << std::endl;
|
|
os << space( indent ) << "prefixLength: " << prefixLength_ << std::endl;
|
|
os << space( indent ) << "prefixBytes[8]: " << static_cast<unsigned>( prefixBytes_[0] ) << " "
|
|
<< static_cast<unsigned>( prefixBytes_[1] ) << " " << static_cast<unsigned>( prefixBytes_[2] ) << " "
|
|
<< static_cast<unsigned>( prefixBytes_[3] ) << " " << static_cast<unsigned>( prefixBytes_[4] ) << " "
|
|
<< static_cast<unsigned>( prefixBytes_[5] ) << " " << static_cast<unsigned>( prefixBytes_[6] ) << " "
|
|
<< static_cast<unsigned>( prefixBytes_[7] ) << std::endl;
|
|
os << space( indent ) << "nBytesPrefixRead: " << nBytesPrefixRead_ << std::endl;
|
|
os << space( indent ) << "stringLength: " << stringLength_ << std::endl;
|
|
os << space( indent )
|
|
<< "currentString: "
|
|
""
|
|
<< currentString_
|
|
<< ""
|
|
""
|
|
<< std::endl;
|
|
os << space( indent ) << "nBytesStringRead: " << nBytesStringRead_ << std::endl;
|
|
}
|
|
#endif
|
|
|
|
//================================================================
|
|
|
|
template <typename RegisterT>
|
|
BitpackIntegerDecoder<RegisterT>::BitpackIntegerDecoder( bool isScaledInteger, unsigned bytestreamNumber,
|
|
SourceDestBuffer &dbuf, int64_t minimum, int64_t maximum,
|
|
double scale, double offset, uint64_t maxRecordCount ) :
|
|
BitpackDecoder( bytestreamNumber, dbuf, sizeof( RegisterT ), maxRecordCount ),
|
|
isScaledInteger_( isScaledInteger ), minimum_( minimum ), maximum_( maximum ), scale_( scale ), offset_( offset )
|
|
{
|
|
/// Get pointer to parent ImageFileImpl
|
|
ImageFileImplSharedPtr imf( dbuf.impl()->destImageFile() ); //??? should be function for this,
|
|
// imf->parentFile() --> ImageFile?
|
|
|
|
bitsPerRecord_ = imf->bitsNeeded( minimum_, maximum_ );
|
|
destBitMask_ = ( bitsPerRecord_ == 64 ) ? ~0 : static_cast<RegisterT>( 1ULL << bitsPerRecord_ ) - 1;
|
|
}
|
|
|
|
template <typename RegisterT>
|
|
size_t BitpackIntegerDecoder<RegisterT>::inputProcessAligned( const char *inbuf, const size_t firstBit,
|
|
const size_t endBit )
|
|
{
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "BitpackIntegerDecoder::inputProcessAligned() called, inbuf=" << (void *)( inbuf )
|
|
<< " firstBit=" << firstBit << " endBit=" << endBit << std::endl;
|
|
#endif
|
|
|
|
/// Read from inbuf, decode, store in destBuffer
|
|
/// Repeat until have filled destBuffer, or completed all records
|
|
|
|
#ifdef E57_DEBUG
|
|
#if 0 // I know now way to do this portably
|
|
// Deactivate for now until a better solution is found.
|
|
/// Verify that inbuf is naturally aligned to RegisterT boundary (1, 2, 4,or 8 bytes). Base class is doing this for us.
|
|
if ((reinterpret_cast<unsigned>(inbuf)) % sizeof(RegisterT))
|
|
throw E57_EXCEPTION2(E57_ERROR_INTERNAL, "inbuf=" + toString(reinterpret_cast<unsigned>(inbuf)));
|
|
#endif
|
|
/// Verfiy first bit is in first word
|
|
if ( firstBit >= 8 * sizeof( RegisterT ) )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "firstBit=" + toString( firstBit ) );
|
|
}
|
|
#endif
|
|
|
|
size_t destRecords = destBuffer_->capacity() - destBuffer_->nextIndex();
|
|
|
|
/// Precalculate exact number of full records that are in inbuf
|
|
/// We can handle the case where don't have a full word at end of inbuf, but
|
|
/// all the bits of the record are there;
|
|
size_t bitCount = endBit - firstBit;
|
|
size_t maxInputRecords = bitCount / bitsPerRecord_;
|
|
|
|
/// Number of transfers is the smaller of what was requested and what is
|
|
/// available in input.
|
|
size_t recordCount = std::min( destRecords, maxInputRecords );
|
|
|
|
// Can't process more than defined in input file
|
|
if ( static_cast<uint64_t>( recordCount ) > maxRecordCount_ - currentRecordIndex_ )
|
|
{
|
|
recordCount = static_cast<unsigned>( maxRecordCount_ - currentRecordIndex_ );
|
|
}
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " recordCount=" << recordCount << std::endl;
|
|
#endif
|
|
|
|
auto inp = reinterpret_cast<const RegisterT *>( inbuf );
|
|
unsigned wordPosition = 0; /// The index in inbuf of the word we are currently working on.
|
|
|
|
/// For example on little endian machine:
|
|
/// Assume: registerT=uint32_t, bitOffset=20, destBitMask=0x00007fff (for a
|
|
/// 15 bit value). inp[wordPosition] LLLLLLLL LLLLXXXX
|
|
/// XXXXXXXX XXXXXXXX Note LSB of value is at bit20 inp(wordPosition+1]
|
|
/// XXXXXXXX XXXXXXXX XXXXXXXX XXXXXHHH H=high bits of value,
|
|
/// X=uninteresting bits low = inp[i] >> bitOffset 00000000
|
|
/// 00000000 0000LLLL LLLLLLLL L=low bits of value, X=uninteresting bits
|
|
/// high = inp[i+1] << (32-bitOffset) XXXXXXXX XXXXXXXX XHHH0000 00000000
|
|
/// w = high | low XXXXXXXX XXXXXXXX XHHHLLLL LLLLLLLL destBitmask 00000000
|
|
/// 00000000 01111111 11111111 w & mask 00000000
|
|
/// 00000000 0HHHLLLL LLLLLLLL
|
|
|
|
size_t bitOffset = firstBit;
|
|
|
|
for ( size_t i = 0; i < recordCount; i++ )
|
|
{
|
|
/// Get lower word (contains at least the LSbit of the value),
|
|
RegisterT low = inp[wordPosition];
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " bitOffset: " << bitOffset << std::endl;
|
|
std::cout << " low: " << binaryString( low ) << std::endl;
|
|
#endif
|
|
|
|
RegisterT w;
|
|
if ( bitOffset > 0 )
|
|
{
|
|
/// Get upper word (may or may not contain interesting bits),
|
|
RegisterT high = inp[wordPosition + 1];
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " high:" << binaryString( high ) << std::endl;
|
|
#endif
|
|
|
|
/// Shift high to just above the lower bits, shift low LSBit to bit0,
|
|
/// OR together. Note shifts are logical (not arithmetic) because using
|
|
/// unsigned variables.
|
|
w = ( high << ( 8 * sizeof( RegisterT ) - bitOffset ) ) | ( low >> bitOffset );
|
|
}
|
|
else
|
|
{
|
|
/// The left shift (used above) is not defined if shift is >= size of
|
|
/// word
|
|
w = low;
|
|
}
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " w: " << binaryString( w ) << std::endl;
|
|
#endif
|
|
|
|
/// Mask off uninteresting bits
|
|
w &= destBitMask_;
|
|
|
|
/// Add minimum_ to value to get back what writer originally sent
|
|
int64_t value = minimum_ + static_cast<uint64_t>( w );
|
|
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " Storing value=" << value << std::endl;
|
|
#endif
|
|
|
|
/// The parameter isScaledInteger_ determines which version of
|
|
/// setNextInt64 gets called
|
|
if ( isScaledInteger_ )
|
|
{
|
|
destBuffer_->setNextInt64( value, scale_, offset_ );
|
|
}
|
|
else
|
|
{
|
|
destBuffer_->setNextInt64( value );
|
|
}
|
|
|
|
/// Store the result in next available position in the user's dest buffer
|
|
|
|
/// Calc next bit alignment and which word it starts in
|
|
bitOffset += bitsPerRecord_;
|
|
if ( bitOffset >= 8 * sizeof( RegisterT ) )
|
|
{
|
|
bitOffset -= 8 * sizeof( RegisterT );
|
|
wordPosition++;
|
|
}
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << " Processed " << i + 1 << " records, wordPosition=" << wordPosition << " decoder:" << std::endl;
|
|
dump( 4 );
|
|
#endif
|
|
}
|
|
|
|
/// Update counts of records processed
|
|
currentRecordIndex_ += recordCount;
|
|
|
|
/// Return number of bits processed.
|
|
return ( recordCount * bitsPerRecord_ );
|
|
}
|
|
|
|
#ifdef E57_DEBUG
|
|
template <typename RegisterT> void BitpackIntegerDecoder<RegisterT>::dump( int indent, std::ostream &os )
|
|
{
|
|
BitpackDecoder::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 ) << "destBitMask: " << binaryString( destBitMask_ ) << " = " << hexString( destBitMask_ )
|
|
<< std::endl;
|
|
}
|
|
#endif
|
|
|
|
//================================================================
|
|
|
|
ConstantIntegerDecoder::ConstantIntegerDecoder( bool isScaledInteger, unsigned bytestreamNumber, SourceDestBuffer &dbuf,
|
|
int64_t minimum, double scale, double offset,
|
|
uint64_t maxRecordCount ) :
|
|
Decoder( bytestreamNumber ),
|
|
maxRecordCount_( maxRecordCount ), destBuffer_( dbuf.impl() ), isScaledInteger_( isScaledInteger ),
|
|
minimum_( minimum ), scale_( scale ), offset_( offset )
|
|
{
|
|
}
|
|
|
|
void ConstantIntegerDecoder::destBufferSetNew( std::vector<SourceDestBuffer> &dbufs )
|
|
{
|
|
if ( dbufs.size() != 1 )
|
|
{
|
|
throw E57_EXCEPTION2( E57_ERROR_INTERNAL, "dbufsSize=" + toString( dbufs.size() ) );
|
|
}
|
|
|
|
destBuffer_ = dbufs.at( 0 ).impl();
|
|
}
|
|
|
|
size_t ConstantIntegerDecoder::inputProcess( const char *source, const size_t availableByteCount )
|
|
{
|
|
(void)source; (void)availableByteCount;
|
|
#ifdef E57_MAX_VERBOSE
|
|
std::cout << "ConstantIntegerDecoder::inputprocess() called, source=" << (void *)( source )
|
|
<< " availableByteCount=" << availableByteCount << std::endl;
|
|
#endif
|
|
|
|
/// We don't need any input bytes to produce output, so ignore source and
|
|
/// availableByteCount.
|
|
|
|
/// Fill dest buffer unless get to maxRecordCount
|
|
size_t count = destBuffer_->capacity() - destBuffer_->nextIndex();
|
|
uint64_t remainingRecordCount = maxRecordCount_ - currentRecordIndex_;
|
|
if ( static_cast<uint64_t>( count ) > remainingRecordCount )
|
|
{
|
|
count = static_cast<unsigned>( remainingRecordCount );
|
|
}
|
|
|
|
if ( isScaledInteger_ )
|
|
{
|
|
for ( size_t i = 0; i < count; i++ )
|
|
{
|
|
destBuffer_->setNextInt64( minimum_, scale_, offset_ );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( size_t i = 0; i < count; i++ )
|
|
{
|
|
destBuffer_->setNextInt64( minimum_ );
|
|
}
|
|
}
|
|
currentRecordIndex_ += count;
|
|
return ( count );
|
|
}
|
|
|
|
void ConstantIntegerDecoder::stateReset()
|
|
{
|
|
}
|
|
|
|
#ifdef E57_DEBUG
|
|
void ConstantIntegerDecoder::dump( int indent, std::ostream &os )
|
|
{
|
|
os << space( indent ) << "bytestreamNumber: " << bytestreamNumber_ << std::endl;
|
|
os << space( indent ) << "currentRecordIndex: " << currentRecordIndex_ << std::endl;
|
|
os << space( indent ) << "maxRecordCount: " << maxRecordCount_ << std::endl;
|
|
os << space( indent ) << "isScaledInteger: " << isScaledInteger_ << std::endl;
|
|
os << space( indent ) << "minimum: " << minimum_ << std::endl;
|
|
os << space( indent ) << "scale: " << scale_ << std::endl;
|
|
os << space( indent ) << "offset: " << offset_ << std::endl;
|
|
os << space( indent ) << "destBuffer:" << std::endl;
|
|
destBuffer_->dump( indent + 4, os );
|
|
}
|
|
#endif
|