4.2.1.2 Sample Code

The following C++ code implements a sample decompressor for RDP 8.0 Bulk Compression. Error handling has been omitted for clarity.

 #include <memory.h>  // for memcpy()
  
 // RDP8 definitions
  
 typedef unsigned __int8  byte;
 typedef unsigned __int16 uint16;
 typedef unsigned __int32 uint32;
  
 #pragma pack(push, 1)
  
 typedef struct
 {
     byte    descriptor;
     uint16  segmentCount;
     uint32  uncompressedSize;
 //  RDP_DATA_SEGMENT first;
 } RDP_SEGMENTED_DATA;
  
 // descriptor values
 #define SEGMENTED_SINGLE        ( 0xE0 )
 #define SEGMENTED_MULTIPART     ( 0xE1 )
  
 typedef struct
 {
     uint32  size;
 //  byte    data[size];
 } RDP_DATA_SEGMENT;
  
 #pragma pack(pop)
  
 #define PACKET_COMPRESSED       ( 0x20 )
 #define PACKET_COMPR_TYPE_RDP8  ( 0x04 )
  
 // token assignments from the spec, sorted by prefixLength
  
 typedef struct
 {
     int     prefixLength;     // number of bits in the prefix
     int     prefixCode;       // bit pattern of this prefix
     int     valueBits;        // number of value bits to read
     int     tokenType;        // 0=literal, 1=match
     uint32  valueBase;        // added to the value bits
 } Token;
  
 const Token tokenTable[] =
 {
     // len code vbits type  vbase
     {  1,    0,   8,   0,           0 },    // 0
     {  5,   17,   5,   1,           0 },    // 10001
     {  5,   18,   7,   1,          32 },    // 10010
     {  5,   19,   9,   1,         160 },    // 10011
     {  5,   20,  10,   1,         672 },    // 10100
     {  5,   21,  12,   1,        1696 },    // 10101
     {  5,   24,   0,   0,  0x00       },    // 11000
     {  5,   25,   0,   0,  0x01       },    // 11001
     {  6,   44,  14,   1,        5792 },    // 101100
     {  6,   45,  15,   1,       22176 },    // 101101
     {  6,   52,   0,   0,  0x02       },    // 110100
     {  6,   53,   0,   0,  0x03       },    // 110101
     {  6,   54,   0,   0,  0xFF       },    // 110110
     {  7,   92,  18,   1,       54944 },    // 1011100
     {  7,   93,  20,   1,      317088 },    // 1011101
     {  7,  110,   0,   0,  0x04       },    // 1101110
     {  7,  111,   0,   0,  0x05       },    // 1101111
     {  7,  112,   0,   0,  0x06       },    // 1110000
     {  7,  113,   0,   0,  0x07       },    // 1110001
     {  7,  114,   0,   0,  0x08       },    // 1110010
     {  7,  115,   0,   0,  0x09       },    // 1110011
     {  7,  116,   0,   0,  0x0A       },    // 1110100
     {  7,  117,   0,   0,  0x0B       },    // 1110101
     {  7,  118,   0,   0,  0x3A       },    // 1110110
     {  7,  119,   0,   0,  0x3B       },    // 1110111
     {  7,  120,   0,   0,  0x3C       },    // 1111000
     {  7,  121,   0,   0,  0x3D       },    // 1111001
     {  7,  122,   0,   0,  0x3E       },    // 1111010
     {  7,  123,   0,   0,  0x3F       },    // 1111011
     {  7,  124,   0,   0,  0x40       },    // 1111100
     {  7,  125,   0,   0,  0x80       },    // 1111101
     {  8,  188,  20,   1,     1365664 },    // 10111100
     {  8,  189,  21,   1,     2414240 },    // 10111101
     {  8,  252,   0,   0,  0x0C       },    // 11111100
     {  8,  253,   0,   0,  0x38       },    // 11111101
     {  8,  254,   0,   0,  0x39       },    // 11111110
     {  8,  255,   0,   0,  0x66       },    // 11111111
     {  9,  380,  22,   1,     4511392 },    // 101111100
     {  9,  381,  23,   1,     8705696 },    // 101111101
     {  9,  382,  24,   1,    17094304 },    // 101111110
     { 0 }
 };
  
  
 class Rdp8Decompressor
 {
 public:
  
     Rdp8Decompressor()
     {
         m_historyIndex = 0;
     }
  
     // input buffer
     byte *  m_pbInputCurrent;           // ptr into input bytes
     byte *  m_pbInputEnd;               // ptr past end of input
  
     // input bit stream
     uint32  m_cBitsRemaining;           // # bits input remaining
     uint32  m_BitsCurrent;              // remainder of most-recent byte
     uint32  m_cBitsCurrent;             // number of bits in m_BitsCurrent
  
     // decompressed output
     byte    m_outputBuffer[65536];      // most-recent Decompress result
     uint32  m_outputCount;              // length in m_outputBuffer
  
     // decompression history
     byte    m_historyBuffer[2500000];   // last N bytes of output
     uint32  m_historyIndex;             // index for next byte out
  
  
     // decompress, return data in an allocated buffer
  
     void Decompress(
         byte *  pbInput,
         int     cbInput,
         byte ** ppbOutput,
         int *   pcbOutput
         )
     {
         RDP_SEGMENTED_DATA * pSegmentedData = (RDP_SEGMENTED_DATA *) pbInput;
  
         if (pSegmentedData->descriptor == SEGMENTED_SINGLE)
         {
             OutputFromSegment(pbInput + 1, cbInput - 1);
  
             *ppbOutput = new byte[m_outputCount];
             *pcbOutput = m_outputCount;
             memcpy(*ppbOutput, m_outputBuffer, m_outputCount);
         }
         else if (pSegmentedData->descriptor == SEGMENTED_MULTIPART)
         {
             uint32 segmentOffset = sizeof(RDP_SEGMENTED_DATA);
             byte * pConcatenated = new byte[pSegmentedData->uncompressedSize];
             *ppbOutput = pConcatenated;
             *pcbOutput = pSegmentedData->uncompressedSize;
  
             for (uint16 segmentNumber = 0;
                 segmentNumber < pSegmentedData->segmentCount;
                 segmentNumber++)
             {
                 RDP_DATA_SEGMENT * pSegment =
                         (RDP_DATA_SEGMENT *) (pbInput + segmentOffset);
  
                 OutputFromSegment(
                         pbInput + segmentOffset + sizeof(RDP_DATA_SEGMENT),
                         pSegment->size);
  
                 segmentOffset += sizeof(RDP_DATA_SEGMENT) + pSegment->size;
                 memcpy(pConcatenated, m_outputBuffer, m_outputCount);
                 pConcatenated += m_outputCount;
             }
         }
     }
  
     // decompress one segment into m_outputBuffer
  
     void OutputFromSegment(
         byte *  pbSegment,
         int     cbSegment
         )
     {
         if (pbSegment[0] & PACKET_COMPRESSED)
         {
             OutputFromCompressed(pbSegment + 1, cbSegment - 1);
         }
         else
         {
             OutputFromNotCompressed(pbSegment + 1, cbSegment - 1);
         }
     }
  
     // decompress an unencoded segment into m_outputBuffer
  
     void OutputFromNotCompressed(
         byte *  pbRaw,
         int     cbRaw
         )
     {
         m_outputCount = 0;
  
         for (int iRaw = 0; iRaw < cbRaw; iRaw++)
         {
             byte c = pbRaw[iRaw];
  
             m_historyBuffer[m_historyIndex++] = c;
             if (m_historyIndex == sizeof(m_historyBuffer))
             {
                 m_historyIndex = 0;
             }
  
             m_outputBuffer[m_outputCount++] = c;
         }
     }
  
     // decompress a Huffman-encoded segment into m_outputBuffer
  
     void OutputFromCompressed(
         byte *  pbEncoded,
         int     cbEncoded
         )
     {
         m_outputCount = 0;
  
         m_pbInputCurrent = pbEncoded;
         m_pbInputEnd = pbEncoded + cbEncoded - 1;
  
         m_cBitsRemaining = 8 * (cbEncoded - 1) - *m_pbInputEnd;
         m_cBitsCurrent = 0;
         m_BitsCurrent = 0;
  
         while (m_cBitsRemaining)
         {
             int haveBits = 0;
             int inPrefix = 0;
  
             byte c;
             uint32 count;
             uint32 distance;
  
             // Scan the token table, considering more bits as needed,
             // until the resulting token is found.
  
             for (int opIndex = 0; 
                 tokenTable[opIndex].prefixLength != 0;
                 opIndex++)
             {
                 // get more bits if needed
                 while (haveBits < tokenTable[opIndex].prefixLength)
                 {
                     inPrefix = (inPrefix << 1) + GetBits(1);
                     haveBits++;
                 }
  
                 if (inPrefix == tokenTable[opIndex].prefixCode)
                 {
                     if (tokenTable[opIndex].tokenType == 0)
                     {
                         c = (byte)(tokenTable[opIndex].valueBase +
                                 GetBits(tokenTable[opIndex].valueBits));
                         goto output_literal;
                     }
                     else
                     {
                         distance = tokenTable[opIndex].valueBase +
                                 GetBits(tokenTable[opIndex].valueBits);
                         if (distance != 0)
                         {
                             if (GetBits(1) == 0)
                             {
                                 count = 3;
                             }
                             else
                             {
                                 count = 4;
                                 int extra = 2;
                                 while (GetBits(1) == 1)
                                 {
                                     count *= 2;
                                     extra++;
                                 }
  
                                 count += GetBits(extra);
                             }
                             goto output_match;
                         }
                         else // match distance == 0 is special case
                         {
                             count = GetBits(15);
  
                             // discard remaining bits
                             m_cBitsRemaining -= m_cBitsCurrent;
                             m_cBitsCurrent = 0;
                             m_BitsCurrent = 0;
                             goto output_unencoded;
                         }
                     }
                 }
             }
             break;
  
         output_literal:
  
             // Add one byte 'c' to output and history
             m_historyBuffer[m_historyIndex] = c;
             if (++m_historyIndex == sizeof(m_historyBuffer))
             {
                 m_historyIndex = 0;
             }
             m_outputBuffer[m_outputCount++] = c;
             continue;
  
         output_match:
  
             // Add 'count' bytes from 'distance' back in history.
             // Output these bytes again, and add to history again.
             uint32 prevIndex =
                     m_historyIndex + sizeof(m_historyBuffer) - distance;
             prevIndex = prevIndex % sizeof(m_historyBuffer);
  
             // n.b. memcpy or movsd, for example, will not work here.
             // Overlapping matches have to replicate.  movsb might work.
             while (count--)
             {
                 c = m_historyBuffer[prevIndex];
                 if (++prevIndex == sizeof(m_historyBuffer))
                 {
                     prevIndex = 0;
                 }
  
                 m_historyBuffer[m_historyIndex] = c;
                 if (++m_historyIndex == sizeof(m_historyBuffer))
                 {
                     m_historyIndex = 0;
                 }
  
                 m_outputBuffer[m_outputCount] = c;
                 ++m_outputCount;
             }
             continue;
  
         output_unencoded:
  
             // Copy 'count' bytes from stream input to output
             // and add to history.
             while (count--)
             {
                 c = *m_pbInputCurrent++;
                 m_cBitsRemaining -= 8;
  
                 m_historyBuffer[m_historyIndex] = c;
                 if (++m_historyIndex == sizeof(m_historyBuffer))
                 {
                     m_historyIndex = 0;
                 }
  
                 m_outputBuffer[m_outputCount] = c;
                 ++m_outputCount;
             }
             continue;
         }
     }
  
     //  Return the value of the next 'bitCount' bits as unsigned.
     uint32 GetBits(
         uint32 bitCount
         )
     {
         while (m_cBitsCurrent < bitCount)
         {
             m_BitsCurrent <<= 8;
             if (m_pbInputCurrent < m_pbInputEnd)
             {
                 m_BitsCurrent += *m_pbInputCurrent++;
             }
             m_cBitsCurrent += 8;
         }
  
         m_cBitsRemaining -= bitCount;
         m_cBitsCurrent -= bitCount;
  
         uint32 result = m_BitsCurrent >> m_cBitsCurrent;
  
         m_BitsCurrent &= ((1 << m_cBitsCurrent) - 1);
  
         return result;
     }
 };