Example #1
0
 /**
  * Uncompress bytes.
  *
  * @param bool $reset Reset the LXZ state?
  * @param BitReader $reader The reader that provides the data.
  * @param int $numberOfBytes The number of decompressed bytes to retrieve.
  *
  * @throws Exception Throws an Exception in case of errors.
  *
  * @return string
  */
 public function inflate($reset, BitReader $reader, $numberOfBytes)
 {
     if ($reset) {
         $this->r2 = $this->r1 = $this->r0 = 1;
         $this->headerRead = false;
         $this->framesRead = 0;
         $this->remainingInBlock = 0;
         $this->blockType = null;
         $this->intelCurrentPosition = 0;
         $this->intelStarted = false;
         $this->windowPosition = 0;
         $this->mainTree->clear();
         $this->lengthTree->clear();
     }
     if ($this->headerRead === false) {
         if ($reader->readLE(1) > 0) {
             $this->intelFilesize = $reader->readLE(16) << 16 | $reader->readLE(16);
         }
         $this->headerRead = true;
     }
     $togo = $numberOfBytes;
     while ($togo > 0) {
         if ($this->remainingInBlock === 0) {
             if ($this->blockType === static::BLOCKTYPE_UNCOMPRESSED) {
                 if (($this->blockLength & 1) !== 0) {
                     $reader->skip(1);
                 }
             }
             $this->blockType = $reader->readLE(3);
             $this->remainingInBlock = $this->blockLength = $reader->readLE(16) << 8 | $reader->readLE(8);
             switch ($this->blockType) {
                 case static::BLOCKTYPE_ALIGNED:
                     $this->alignedTree->readAlignLengthTable($reader);
                     $this->alignedTree->makeSymbolTable();
                     /* @noinspection PhpMissingBreakStatementInspection */
                 /* @noinspection PhpMissingBreakStatementInspection */
                 case static::BLOCKTYPE_VERBATIM:
                     $this->mainTree->readLengthTable($reader, 0, static::NUM_CHARS);
                     $this->mainTree->readLengthTable($reader, static::NUM_CHARS, $this->mainElements);
                     $this->mainTree->makeSymbolTable();
                     if ($this->mainTree->isIntel()) {
                         $this->intelStarted = true;
                     }
                     $this->lengthTree->readLengthTable($reader, 0, static::NUM_SECONDARY_LENGTHS);
                     $this->lengthTree->makeSymbolTable();
                     break;
                 case static::BLOCKTYPE_UNCOMPRESSED:
                     $this->intelStarted = true;
                     if ($reader->ensure(16) > 16) {
                         $reader->skip(-2);
                     }
                     $this->r0 = $reader->readUInt32();
                     $this->r1 = $reader->readUInt32();
                     $this->r2 = $reader->readUInt32();
                     break;
                 default:
                     throw new Exception('Unexpected block type ' . $this->blockType);
             }
         }
         while (($thisRun = $this->remainingInBlock) > 0 && $togo > 0) {
             if ($thisRun > $togo) {
                 $thisRun = $togo;
             }
             $togo -= $thisRun;
             $this->remainingInBlock -= $thisRun;
             $this->windowPosition %= $this->windowSize;
             if ($this->windowPosition + $thisRun > $this->windowSize) {
                 throw new Exception('Trying to read more that window size bytes');
             }
             if ($this->blockType === static::BLOCKTYPE_UNCOMPRESSED) {
                 $this->window = $reader->readFully($this->window, $this->windowPosition, $thisRun);
                 $this->windowPosition += $thisRun;
             } else {
                 while ($thisRun > 0) {
                     $mainElement = $this->mainTree->readHuffmanSymbol($reader);
                     if ($mainElement < static::NUM_CHARS) {
                         $this->window[$this->windowPosition++] = $mainElement;
                         --$thisRun;
                     } else {
                         $mainElement -= static::NUM_CHARS;
                         $matchLength = $mainElement & static::NUM_PRIMARY_LENGTHS;
                         if ($matchLength === static::NUM_PRIMARY_LENGTHS) {
                             $matchLength += $this->lengthTree->readHuffmanSymbol($reader);
                         }
                         $matchLength += static::MIN_MATCH;
                         $matchOffset = $mainElement >> 3;
                         switch ($matchOffset) {
                             case 0:
                                 $matchOffset = $this->r0;
                                 break;
                             case 1:
                                 $matchOffset = $this->r1;
                                 $this->r1 = $this->r0;
                                 $this->r0 = $matchOffset;
                                 break;
                             case 2:
                                 $matchOffset = $this->r2;
                                 $this->r2 = $this->r0;
                                 $this->r0 = $matchOffset;
                                 break;
                             default:
                                 switch ($this->blockType) {
                                     case static::BLOCKTYPE_VERBATIM:
                                         if ($matchOffset !== 3) {
                                             $extra = static::$EXTRA_BITS[$matchOffset];
                                             $matchOffset = static::$POSITION_BASE[$matchOffset] - 2 + $reader->readLE($extra);
                                         } else {
                                             $matchOffset = 1;
                                         }
                                         break;
                                     case static::BLOCKTYPE_ALIGNED:
                                         $extra = static::$EXTRA_BITS[$matchOffset];
                                         $matchOffset = static::$POSITION_BASE[$matchOffset] - 2;
                                         switch ($extra) {
                                             case 0:
                                                 $matchOffset = 1;
                                                 break;
                                             case 1:
                                             case 2:
                                                 // verbatim bits only
                                                 $matchOffset += $reader->readLE($extra);
                                                 break;
                                             case 3:
                                                 // aligned bits only
                                                 $matchOffset += $this->alignedTree->readHuffmanSymbol($reader);
                                                 break;
                                             default:
                                                 // verbatim and aligned bits
                                                 $extra -= 3;
                                                 $matchOffset += $reader->readLE($extra) << 3;
                                                 $matchOffset += $this->alignedTree->readHuffmanSymbol($reader);
                                                 break;
                                         }
                                         break;
                                     default:
                                         throw new Exception('Unexpected block type ' + $this->blockType);
                                 }
                                 $this->r2 = $this->r1;
                                 $this->r1 = $this->r0;
                                 $this->r0 = $matchOffset;
                                 break;
                         }
                         $runSrc = 0;
                         $runDest = $this->windowPosition;
                         $thisRun -= $matchLength;
                         // copy any wrapped around source data
                         if ($this->windowPosition >= $matchOffset) {
                             // no wrap
                             $runSrc = $runDest - $matchOffset;
                         } else {
                             // wrap around
                             $runSrc = $runDest + ($this->windowSize - $matchOffset);
                             $copyLength = $matchOffset - $this->windowPosition;
                             if ($copyLength < $matchLength) {
                                 $matchLength -= $copyLength;
                                 $this->windowPosition += $copyLength;
                                 while ($copyLength-- > 0) {
                                     $this->window[$runDest++] = $this->window[$runSrc++];
                                 }
                                 $runSrc = 0;
                             }
                         }
                         $this->windowPosition += $matchLength;
                         // copy match data - no worries about destination wraps
                         while ($matchLength-- > 0) {
                             $this->window[$runDest++] = $this->window[$runSrc++];
                         }
                     }
                 }
             }
         }
     }
     if ($togo !== 0) {
         throw new Exception('should never happens');
     }
     $result = array_slice($this->window, ($this->windowPosition === 0 ? $this->windowSize : $this->windowPosition) - $numberOfBytes, $numberOfBytes);
     // Intel E8 decoding
     if ($this->intelFilesize !== 0) {
         if ($this->framesRead++ < 32768) {
             if ($numberOfBytes <= 6 || $this->intelStarted === false) {
                 $this->intelCurrentPosition += $numberOfBytes;
             } else {
                 $currentPosition = $this->intelCurrentPosition;
                 $this->intelCurrentPosition += $numberOfBytes;
                 for ($i = 0; $i < $numberOfBytes - 10;) {
                     if ($result[$i++] !== 0xe8) {
                         ++$currentPosition;
                     } else {
                         $absoluteOffset = $result[$i] & 0xff | ($result[$i + 1] & 0xff) << 8 | ($result[$i + 2] & 0xff) << 16 | ($result[$i + 3] & 0xff) << 24;
                         if ($absoluteOffset >= -$currentPosition && $absoluteOffset < $this->intelFilesize) {
                             $referenceOffset = $absoluteOffset >= 0 ? $absoluteOffset - $currentPosition : $absoluteOffset + $this->intelFilesize;
                             $result[$i] = $referenceOffset;
                             $result[$i + 1] = $referenceOffset >> 8;
                             $result[$i + 2] = $referenceOffset >> 16;
                             $result[$i + 3] = $referenceOffset >> 24;
                         }
                         $i += 4;
                         $currentPosition += 5;
                     }
                 }
             }
         }
     }
     return implode('', array_map('chr', $result));
 }
Example #2
0
 /**
  * Decode a Huffman symbol from the bitstream using the stated table and return it.
  *
  * @param BitReader $reader The reader that provides the data.
  *
  * @throws Exception Throws an Exception in case of errors.
  *
  * @return int
  */
 public function readHuffmanSymbol(BitReader $reader)
 {
     $next = $reader->peek(16, true);
     $symbol = $this->symbols[$reader->peek($this->bits, true)];
     if ($symbol >= $this->maxSymbol) {
         $j = 1 << 16 - $this->bits;
         do {
             $j >>= 1;
             $symbol <<= 1;
             $symbol |= ($next & $j) > 0 ? 1 : 0;
             $symbol = $this->symbols[$symbol];
         } while ($symbol >= $this->maxSymbol);
     }
     $reader->readLE($this->lens[$symbol]);
     return $symbol;
 }