/**************************************************************************/
/* */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
/* */
/* NXCOMP, NX protocol compression and NX extensions to this software */
/* are copyright of the aforementioned persons and companies. */
/* */
/* Redistribution and use of the present software is allowed according */
/* to terms specified in the file LICENSE.nxcomp which comes in the */
/* source distribution. */
/* */
/* All rights reserved. */
/* */
/* NOTE: This software has received contributions from various other */
/* contributors, only the core maintainers and supporters are listed as */
/* copyright holders. Please contact us, if you feel you should be listed */
/* as copyright holder, as well. */
/* */
/**************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "Misc.h"
#include "Control.h"
#include "DecodeBuffer.h"
//
// Set the verbosity level.
//
#define PANIC
#define WARNING
#undef TEST
#undef DEBUG
#undef DUMP
DecodeBuffer::DecodeBuffer(const unsigned char *data, unsigned int length)
: buffer_(data), end_(buffer_ + length), nextSrc_(buffer_), srcMask_(0x80)
{
// Since ProtoStep7 (#issue 108)
end_ = buffer_ + length - DECODE_BUFFER_POSTFIX_SIZE;
}
int DecodeBuffer::decodeValue(unsigned int &value, unsigned int numBits,
unsigned int blockSize, int endOkay)
{
#ifdef DUMP
*logofs << "DecodeBuffer: Decoding " << numBits
<< " bits value with block " << blockSize
<< " and " << (nextSrc_ - buffer_)
<< " bytes in buffer.\n" << logofs_flush;
#endif
unsigned int result = 0;
unsigned int destMask = 0x1;
unsigned int bitsRead = 0;
if (blockSize == 0)
blockSize = numBits;
unsigned char nextSrcChar = *nextSrc_;
unsigned int numBlocks = 1;
do
{
if (numBlocks == 4)
{
blockSize = numBits;
}
unsigned int bitsToRead = (blockSize > numBits - bitsRead ?
numBits - bitsRead : blockSize);
unsigned int count = 0;
unsigned char lastBit;
do
{
if (nextSrc_ >= end_)
{
if (!endOkay)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [A] "
<< "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
<< " end_ = " << (end_ - buffer_) << ".\n"
<< logofs_flush;
#endif
//
// Label "context" is just used to identify
// the routine which detected the problem in
// present source file.
//
cerr << "Error" << ": Failure decoding data in context [A].\n";
HandleAbort();
}
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [B] "
<< "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
<< " end_ = " << (end_ - buffer_) << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [B].\n";
HandleAbort();
}
lastBit = (nextSrcChar & srcMask_);
if (lastBit)
result |= destMask;
srcMask_ >>= 1;
if (srcMask_ == 0)
{
srcMask_ = 0x80;
nextSrc_++;
nextSrcChar = *nextSrc_;
}
destMask <<= 1;
}
while (bitsToRead > ++count);
bitsRead += bitsToRead;
if (bitsRead < numBits)
{
if (nextSrc_ >= end_)
{
if (!endOkay)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [C] "
<< "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
<< " end_ = " << (end_ - buffer_) << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [C].\n";
HandleAbort();
}
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [D] "
<< "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
<< " end_ = " << (end_ - buffer_) << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [D].\n";
HandleAbort();
}
unsigned char moreData = (nextSrcChar & srcMask_);
srcMask_ >>= 1;
if (srcMask_ == 0)
{
srcMask_ = 0x80;
nextSrc_++;
nextSrcChar = *nextSrc_;
}
if (!moreData)
{
if (lastBit)
{
do
{
result |= destMask;
destMask <<= 1;
}
while (numBits > ++bitsRead);
}
else
bitsRead = numBits;
}
}
blockSize >>= 1;
if (blockSize < 2)
blockSize = 2;
numBlocks++;
}
while (numBits > bitsRead);
value = result;
return 1;
}
int DecodeBuffer::decodeCachedValue(unsigned int &value, unsigned int numBits,
IntCache &cache, unsigned int blockSize,
int endOkay)
{
#ifdef DUMP
*logofs << "DecodeBuffer: Decoding " << numBits
<< " bits cached value with block " << blockSize
<< " and " << (nextSrc_ - buffer_)
<< " bytes in buffer.\n" << logofs_flush;
#endif
if (nextSrc_ >= end_)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [E] "
<< "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
<< " end_ = " << (end_ - buffer_) << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [E].\n";
HandleAbort();
}
unsigned int index = 0;
unsigned char nextSrcChar = *nextSrc_;
while (!(nextSrcChar & srcMask_))
{
index++;
srcMask_ >>= 1;
if (srcMask_ == 0)
{
srcMask_ = 0x80;
nextSrc_++;
if (nextSrc_ >= end_)
{
if (!endOkay)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [F] "
<< "in decodeCachedValue() nextSrc_ = "
<< (nextSrc_ - buffer_) << " end_ = "
<< (end_ - buffer_) << ".\n" << logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [F].\n";
HandleAbort();
}
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [G] "
<< "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
<< " end_ = " << (end_ - buffer_) << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [G].\n";
HandleAbort();
}
nextSrcChar = *nextSrc_;
}
}
srcMask_ >>= 1;
if (srcMask_ == 0)
{
srcMask_ = 0x80;
nextSrc_++;
}
if (index == 2)
{
// Since ProtoStep8 (#issue 108)
blockSize = cache.getBlockSize(blockSize);
if (decodeValue(value, numBits, blockSize, endOkay))
{
cache.insert(value, IntMask[numBits]);
return 1;
}
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] "
<< "in decodeCacheValue() with no value found.\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [H].\n";
HandleAbort();
}
else
{
if (index > 2)
{
index--;
}
if (index > cache.getSize())
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [I] "
<< "in decodeCachedValue() index = " << index
<< " cache size = " << cache.getSize() << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [I].\n";
HandleAbort();
}
value = cache.get(index);
return 1;
}
}
int DecodeBuffer::decodeCachedValue(unsigned char &value, unsigned int numBits,
CharCache &cache, unsigned int blockSize,
int endOkay)
{
#ifdef DUMP
*logofs << "DecodeBuffer: Decoding " << numBits
<< " bits char cached value with block " << blockSize
<< " and " << nextSrc_ - buffer_ << " bytes read out of "
<< end_ - buffer_ << ".\n" << logofs_flush;
#endif
if (nextSrc_ >= end_)
{
#ifdef TEST
*logofs << "DecodeBuffer: End of buffer reached in context [J] with "
<< nextSrc_ - buffer_ << " bytes read out of "
<< end_ - buffer_ << ".\n" << logofs_flush;
#endif
return 0;
}
unsigned int index = 0;
unsigned char nextSrcChar = *nextSrc_;
while (!(nextSrcChar & srcMask_))
{
index++;
srcMask_ >>= 1;
if (srcMask_ == 0)
{
srcMask_ = 0x80;
nextSrc_++;
if (nextSrc_ >= end_)
{
if (!endOkay)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [K] "
<< "in decodeCachedValue() nextSrc_ "
<< (nextSrc_ - buffer_) << " end_ " << (end_ - buffer_)
<< ".\n" << logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [K].\n";
HandleAbort();
}
#ifdef TEST
*logofs << "DecodeBuffer: End of buffer reached in context [L] with "
<< nextSrc_ - buffer_ << " bytes read out of "
<< end_ - buffer_ << ".\n" << logofs_flush;
#endif
return 0;
}
nextSrcChar = *nextSrc_;
}
}
srcMask_ >>= 1;
if (srcMask_ == 0)
{
srcMask_ = 0x80;
nextSrc_++;
}
if (index == 2)
{
unsigned int temp;
if (decodeValue(temp, numBits, blockSize, endOkay))
{
value = (unsigned char) temp;
cache.insert(value);
}
else
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [M] "
<< "in decodeValue() with index = 2.\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [M].\n";
HandleAbort();
}
}
else
{
if (index > 2)
{
index--;
}
if (index > cache.getSize())
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [N] "
<< "in decodeCachedValue() " << "index = " << index
<< " cache size = " << cache.getSize() << ".\n"
<< logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [N].\n";
HandleAbort();
}
value = cache.get(index);
}
return 1;
}
//
// Simply returns a pointer to the correct spot in
// the internal buffer. If the caller needs this
// data to last beyond the lifetime of the internal
// buffer, it must copy the data in its own memory.
//
const unsigned char *DecodeBuffer::decodeMemory(unsigned int numBytes)
{
#ifdef DUMP
*logofs << "DecodeBuffer: Decoding " << numBytes
<< " bytes of memory with " << (nextSrc_ - buffer_)
<< " bytes in buffer.\n" << logofs_flush;
#endif
const unsigned char *result;
//
// Force ourselves to a byte boundary.
// Is up to application to ensure data
// is word aligned when needed.
//
if (srcMask_ != 0x80)
{
srcMask_ = 0x80;
nextSrc_++;
}
result = nextSrc_;
if (numBytes > DECODE_BUFFER_OVERFLOW_SIZE)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Can't decode a buffer of "
<< numBytes << " bytes with limit set to "
<< DECODE_BUFFER_OVERFLOW_SIZE << ".\n"
<< logofs_flush;
*logofs << "DecodeBuffer: PANIC! Assuming failure decoding "
<< "data in context [O].\n" << logofs_flush;
#endif
cerr << "Error" << ": Should never decode buffer of size "
<< "greater than " << DECODE_BUFFER_OVERFLOW_SIZE
<< " bytes.\n";
cerr << "Error" << ": Assuming failure decoding data in "
<< "context [O].\n";
HandleAbort();
}
else if (end_ - nextSrc_ < (int) numBytes)
{
#ifdef PANIC
*logofs << "DecodeBuffer: PANIC! Assertion failed. Error [P] "
<< "in decodeMemory() " << "with length " << numBytes
<< " and " << (end_ - nextSrc_)
<< " bytes remaining.\n" << logofs_flush;
#endif
cerr << "Error" << ": Failure decoding data in context [P].\n";
HandleAbort();
}
nextSrc_ += numBytes;
return result;
}
void DecodeBuffer::decodeActionValue(unsigned char &value, unsigned short &position,
ActionCache &cache)
{
unsigned int t;
decodeCachedValue(t, 15, *(cache.base_[cache.slot_]));
cache.last_ += t;
cache.last_ &= 0x7fff;
value = cache.last_ >> 13;
position = cache.last_ & 0x1fff;
#ifdef DEBUG
*logofs << "DecodeBuffer: Decoded value "
<< (unsigned) value << " and position "
<< position << " with base " << cache.slot_
<< ".\n" << logofs_flush;
#endif
#ifdef DEBUG
*logofs << "DecodeBuffer: Action block prediction is "
<< (*(cache.base_[cache.slot_])).getBlockSize(15)
<< ".\n" << logofs_flush;
#endif
cache.slot_ = (cache.last_ & 0xff);
}
void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId,
IntCache &lastIdCache, IntCache &cache,
FreeCache &freeCache)
{
decodeCachedValue(value, 29, lastIdCache);
lastId += (value + 1);
lastId &= 0x1fffffff;
value = lastId;
cache.push(value, 0x1fffffff);
freeCache.push(value, 0x1fffffff);
}
void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId,
IntCache &lastIdCache, XidCache &cache,
FreeCache &freeCache)
{
decodeCachedValue(value, 29, lastIdCache);
#ifdef DEBUG
*logofs << "DecodeBuffer: Decoded new Xid difference "
<< value << ".\n" << logofs_flush;
#endif
lastId += (value + 1);
lastId &= 0x1fffffff;
value = lastId;
unsigned int t = (value - cache.last_);
cache.last_ = value;
#ifdef DEBUG
*logofs << "DecodeBuffer: Decoded new Xid " << value
<< " with base " << cache.slot_ << ".\n"
<< logofs_flush;
#endif
cache.slot_ = (value & 0xff);
cache.base_[cache.slot_] -> push(t, 0x1fffffff);
freeCache.push(value, 0x1fffffff);
}
void DecodeBuffer::decodeXidValue(unsigned int &value, XidCache &cache)
{
unsigned int t;
decodeCachedValue(t, 29, *(cache.base_[cache.slot_]));
cache.last_ += t;
cache.last_ &= 0x1fffffff;
value = cache.last_;
#ifdef DEBUG
*logofs << "DecodeBuffer: Decoded Xid " << value
<< " with base " << cache.slot_ << ".\n"
<< logofs_flush;
#endif
cache.slot_ = (value & 0xff);
#ifdef DEBUG
*logofs << "DecodeBuffer: Xid block prediction is "
<< (*(cache.base_[cache.slot_])).getBlockSize(29)
<< ".\n" << logofs_flush;
#endif
}
void DecodeBuffer::decodeFreeXidValue(unsigned int &value, FreeCache &cache)
{
decodeCachedValue(value, 29, cache);
}