text: // 
  // (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use. 
  // 
  
  #ifndef CHARLS_DECODERSTATEGY
  #define CHARLS_DECODERSTATEGY
  
  
  #include "util.h"
  #include "processline.h"
  #include <memory>
  
  // Purpose: Implements encoding to stream of bits. In encoding mode JpegLsCodec inherits from EncoderStrategy
  class DecoderStrategy
  {
  public:
      DecoderStrategy(const JlsParameters& info) :
          _info(info),
          _byteStream(nullptr),
          _readCache(0),
          _validBits(0),
          _position(nullptr),
          _nextFFPosition(nullptr),
          _endPosition(nullptr)
      {
      }
  
      virtual ~DecoderStrategy()
      {
      }
  
      virtual ProcessLine* CreateProcess(ByteStreamInfo rawStreamInfo) = 0;
  
      virtual void SetPresets(const JlsCustomParameters& presets) = 0;
      virtual void DecodeScan(std::unique_ptr<ProcessLine> outputData, const JlsRect& size, ByteStreamInfo* compressedData, bool bCheck) = 0;
  
      void Init(ByteStreamInfo* compressedStream)
      {
          _validBits = 0;
          _readCache = 0;
  
          if (compressedStream->rawStream)
          {
              _buffer.resize(40000);
              _position = static_cast<uint8_t*>(&_buffer[0]);
              _endPosition = _position;
              _byteStream = compressedStream->rawStream;
              AddBytesFromStream();
          }
          else
          {
              _byteStream = nullptr;
              _position = compressedStream->rawData;
              _endPosition = _position + compressedStream->count;
          }
  
          _nextFFPosition = FindNextFF();
          MakeValid();
      }
  
      void AddBytesFromStream()
      {
          if (!_byteStream || _byteStream->sgetc() == std::char_traits<char>::eof())
              return;
  
          std::size_t count = _endPosition - _position;
  
          if (count > 64)
              return;
  
          for (std::size_t i = 0; i < count; ++i)
          {
              _buffer[i] = _position[i];
          }
          std::size_t offset = &_buffer[0] - _position;
  
          _position += offset;
          _endPosition += offset;
          _nextFFPosition += offset;
  
          std::streamsize readbytes = _byteStream->sgetn(reinterpret_cast<char*>(_endPosition), _buffer.size() - count);
          _endPosition += readbytes;
      }
  
      inlinehint void Skip(int32_t length)
      {
          _validBits -= length;
          _readCache = _readCache << length; 
      }
  
  
      void OnLineBegin(int32_t /*cpixel*/, void* /*ptypeBuffer*/, int32_t /*pixelStride*/) 
      {
      }
  
  
      void OnLineEnd(int32_t pixelCount, const void* ptypeBuffer, int32_t pixelStride)
      {
          _processLine->NewLineDecoded(ptypeBuffer, pixelCount, pixelStride);
      }
  
      void EndScan()
      {
          if ((*_position) != 0xFF)
          {
              ReadBit();
  
              if ((*_position) != 0xFF)
                  throw std::system_error(static_cast<int>(charls::ApiResult::TooMuchCompressedData), CharLSCategoryInstance());
          }
  
          if (_readCache != 0)
              throw std::system_error(static_cast<int>(charls::ApiResult::TooMuchCompressedData), CharLSCategoryInstance());
      }
  
      inlinehint bool OptimizedRead()
      {
          // Easy & fast: if there is no 0xFF byte in sight, we can read without bitstuffing
          if (_position < _nextFFPosition - (sizeof(bufType)-1))
          {
              _readCache |= FromBigEndian<sizeof(bufType)>::Read(_position) >> _validBits;
              int bytesToRead = (bufferbits - _validBits) >> 3;
              _position += bytesToRead;
              _validBits += bytesToRead * 8;
              ASSERT(_validBits >= bufferbits - 8);
              return true;
          }
          return false;
      }
  
      typedef std::size_t bufType;
  
      enum
      {
          bufferbits = sizeof( bufType ) * 8
      };
  
      void MakeValid()
      {
          ASSERT(_validBits <=bufferbits - 8);
  
          if (OptimizedRead())
              return;
  
          AddBytesFromStream();
  
          do
          {
              if (_position >= _endPosition)
              {
                  if (_validBits <= 0)
                      throw std::system_error(static_cast<int>(charls::ApiResult::InvalidCompressedData), CharLSCategoryInstance());
  
                  return;
              }
  
              bufType valnew = _position[0];
  
              if (valnew == 0xFF)
              {
                  // JPEG bitstream rule: no FF may be followed by 0x80 or higher
                  if (_position == _endPosition - 1 || (_position[1] & 0x80) != 0)
                  {
                      if (_validBits <= 0)
                          throw std::system_error(static_cast<int>(charls::ApiResult::InvalidCompressedData), CharLSCategoryInstance());
  
                      return;
                  }
              }
  
              _readCache |= valnew << (bufferbits - 8 - _validBits);
              _position += 1;
              _validBits += 8;
  
              if (valnew == 0xFF)
              {
                  _validBits--;
              }
          }
          while (_validBits < bufferbits - 8);
  
          _nextFFPosition = FindNextFF();
          return;
      }
  
      uint8_t* FindNextFF()
      {
          uint8_t* pbyteNextFF = _position;
  
          while (pbyteNextFF < _endPosition)
          {
              if (*pbyteNextFF == 0xFF) 
                  break;
  
              pbyteNextFF++;
          }
  
          return pbyteNextFF;
      }
  
      uint8_t* GetCurBytePos() const
      {
          int32_t validBits = _validBits;
          uint8_t* compressedBytes = _position;
  
          for (;;)
          {
              int32_t cbitLast = compressedBytes[-1] == 0xFF ? 7 : 8;
  
              if (validBits < cbitLast )
                  return compressedBytes;
  
              validBits -= cbitLast; 
              compressedBytes--;
          }
      }
  
      inlinehint int32_t ReadValue(int32_t length)
      {
          if (_validBits < length)
          {
              MakeValid();
              if (_validBits < length)
                  throw std::system_error(static_cast<int>(charls::ApiResult::InvalidCompressedData), CharLSCategoryInstance());
          }
  
          ASSERT(length != 0 && length <= _validBits);
          ASSERT(length < 32);
          int32_t result = int32_t(_readCache >> (bufferbits - length));
          Skip(length);
          return result;
      }
  
      inlinehint int32_t PeekByte()
      {
          if (_validBits < 8)
          {
              MakeValid();
          }
  
          return _readCache >> (bufferbits - 8); 
      }
  
      inlinehint bool ReadBit()
      {
          if (_validBits <= 0)
          {
              MakeValid();
          }
  
          bool bSet = (_readCache & (bufType(1) << (bufferbits - 1))) != 0;
          Skip(1);
          return bSet;
      }
  
      inlinehint int32_t Peek0Bits()
      {
          if (_validBits < 16)
          {
              MakeValid();
          }
          bufType valTest = _readCache;
  
          for (int32_t count = 0; count < 16; count++)
          {
              if ((valTest & (bufType(1) << (bufferbits - 1))) != 0)
                  return count;
  
              valTest <<= 1;
          }
          return -1;
      }
  
      inlinehint int32_t ReadHighbits()
      {
          int32_t count = Peek0Bits();
          if (count >= 0)
          {
              Skip(count + 1);
              return count;
          }
          Skip(15);
  
          for (int32_t highbits = 15; ; highbits++)
          {
              if (ReadBit())
                  return highbits;
          }
      }
  
      int32_t ReadLongValue(int32_t length)
      {
          if (length <= 24)
              return ReadValue(length);
  
          return (ReadValue(length - 24) << 24) + ReadValue(24);
      }
  
  protected:
      JlsParameters _info;
      std::unique_ptr<ProcessLine> _processLine;
  
  private:
      std::vector<uint8_t> _buffer;
      std::basic_streambuf<char>* _byteStream;
  
      // decoding
      bufType _readCache;
      int32_t _validBits;
      uint8_t* _position;
      uint8_t* _nextFFPosition;
      uint8_t* _endPosition;
  };
  
  
  #endif
  