cahyadsn

LZ77 algorithm implementation on PHP

Dec 16th, 2015
380
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.44 KB | None | 0 0
  1. <?php
  2.  
  3. /*
  4.  * The MIT License
  5.  *
  6.  * Copyright (c) 2009 Olle Törnström studiomediatech.com
  7.  *
  8.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  9.  * of this software and associated documentation files (the "Software"), to deal
  10.  * in the Software without restriction, including without limitation the rights
  11.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12.  * copies of the Software, and to permit persons to whom the Software is
  13.  * furnished to do so, subject to the following conditions:
  14.  *
  15.  * The above copyright notice and this permission notice shall be included in
  16.  * all copies or substantial portions of the Software.
  17.  *
  18.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24.  * THE SOFTWARE.
  25.  *
  26.  * CREDIT: From an initial implementation in Python by Diogo Kollross, made
  27.  *         publicly available on http://www.geocities.com/diogok_br/lz77.
  28.  */
  29. /**
  30.  * This class provides simple LZ77 compression and decompression.
  31.  *
  32.  * @author Olle Törnström olle[at]studiomediatech[dot]com
  33.  * @created 2009-02-20
  34.  */
  35. class LZ77 {
  36.   // MEMBER VARIABLES
  37.   private $referencePrefix;
  38.   private $referenceIntBase;
  39.   private $referenceIntFloorCode;
  40.   private $referenceIntCeilCode;
  41.   private $maxStringDistance;
  42.   private $minStringLength;
  43.   private $maxStringLength;
  44.   private $defaultWindowLength;
  45.   private $maxWindowLength;
  46.   // PUBLIC METHODS
  47.   /**
  48.    * Constructor for an LZ77 compressor.
  49.    *
  50.    *<h4>Optional settings</h4>
  51.    *<ul>
  52.    *<li><code>referenceIntBase</code> default 96</li>
  53.    *<li><code>minStringLength</code> default 5</li>
  54.    *<li><code>defaultWindowLength</code> default 144</li>
  55.    *</ul>
  56.    *
  57.    * @param $settings Optional settings array
  58.    */
  59.   public function __construct($settings = null) {
  60.     if ($settings == null)
  61.       $settings = array ();
  62.     extract($settings);
  63.     $this->referencePrefix = '`';
  64.     $this->referenceIntBase = isset ($referenceIntBase) ? $referenceIntBase : 96;
  65.     $this->referenceIntFloorCode = ord(' ');
  66.     print $this->referenceIntFloorCode;
  67.     $this->referenceIntCeilCode = $this->referenceIntFloorCode + $this->referenceIntBase - 1;
  68.     $this->maxStringDistance = pow($this->referenceIntBase, 2) - 1;
  69.     $this->minStringLength = isset ($minStringLength) ? $minStringLength : 5;
  70.     $this->maxStringLength = pow($this->referenceIntBase, 1) - 1 + $this->minStringLength;
  71.     $this->defaultWindowLength = isset ($defaultWindowLength) ? $defaultWindowLength : 144;
  72.     $this->maxWindowLength = $this->maxStringDistance + $this->minStringLength;
  73.   }
  74.   /**
  75.    * Compress some string data using the LZ77 algorithm.
  76.    *
  77.    * @param $data String data to compress
  78.    * @param $windowLength Optional window length
  79.    */
  80.   public function compress($data, $windowLength = null) {
  81.     if ($windowLength == null)
  82.       $windowLength = $this->defaultWindowLength;
  83.     if ($windowLength > $this->maxWindowLength) {
  84.       throw new Exception('Window length too large');
  85.     }
  86.     $compressed = '';
  87.     $pos = 0;
  88.     $lastPos = strlen($data) - $this->minStringLength;
  89.     while ($pos < $lastPos) {
  90.       $searchStart = max($pos - $windowLength, 0);
  91.       $matchLength = $this->minStringLength;
  92.       $foundMatch = false;
  93.       $bestMatch = array (
  94.         'distance' => $this->maxStringDistance,
  95.         'length' => 0
  96.       );
  97.       $newCompressed = null;
  98.       while (($searchStart + $matchLength) < $pos) {
  99.         $isValidMatch = (substr($data, $searchStart, $matchLength) == substr($data, $pos, $matchLength)) && ($matchLength < $this->maxStringLength);
  100.         if ($isValidMatch) {
  101.           $matchLength++;
  102.           $foundMatch = true;
  103.         } else {
  104.           $realMatchLength = $matchLength -1;
  105.           if ($foundMatch && ($realMatchLength > $bestMatch['length'])) {
  106.             $bestMatch['distance'] = $pos - $searchStart - $realMatchLength;
  107.             $bestMatch['length'] = $realMatchLength;
  108.           }
  109.           $matchLength = $this->minStringLength;
  110.           $searchStart++;
  111.           $foundMatch = false;
  112.         }
  113.       }
  114.       if ($bestMatch['length']) {
  115.         $newCompressed = $this->referencePrefix . $this->encodeReferenceInt($bestMatch['distance'], 2) . $this->encodeReferenceLength($bestMatch['length']);
  116.         $pos += $bestMatch['length'];
  117.       } else {
  118.         if (substr($data, $pos, 1) != $this->referencePrefix) {
  119.           $newCompressed = substr($data, $pos, 1);
  120.         } else {
  121.           $newCompressed = $this->referencePrefix . $this->referencePrefix;
  122.         }
  123.         $pos++;
  124.       }
  125.       $compressed .= $newCompressed;
  126.     }
  127.     return ($compressed . str_replace('`', '``', substr($data, $pos)));
  128.   }
  129.   /**
  130.    * Decompresses an LZ77 compressed data string.
  131.    *
  132.    * @param $data An LZ77 data string.
  133.    */
  134.   public function decompress($data) {
  135.     $decompressed = '';
  136.     $pos = 0;
  137.     while ($pos < strlen($data)) {
  138.       $currentChar = substr($data, $pos, 1);
  139.       if ($currentChar != $this->referencePrefix) {
  140.         $decompressed .= $currentChar;
  141.         $pos++;
  142.       } else {
  143.         $nextChar = substr($data, $pos +1, 1);
  144.         if ($nextChar != $this->referencePrefix) {
  145.           $distance = $this->decodeReferenceInt(substr($data, $pos +1, 2), 2);
  146.           $length = $this->decodeReferenceLength(substr($data, $pos +3, 1));
  147.           $decompressed .= substr($decompressed, strlen($decompressed) - $distance - $length, $length);
  148.           $pos += $this->minStringLength - 1;
  149.         } else {
  150.           $decompressed .= $this->referencePrefix;
  151.           $pos += 2;
  152.         }
  153.       }
  154.     }
  155.     return $decompressed;
  156.   }
  157.   // PRIVATE METHODS
  158.   private function encodeReferenceInt($value, $width) {
  159.     if (($value >= 0) && ($value < (pow($this->referenceIntBase, $width) - 1))) {
  160.       $encoded = '';
  161.       while ($value > 0) {
  162.         $encoded = chr(($value % $this->referenceIntBase) + $this->referenceIntFloorCode) . $encoded;
  163.         $value = floor($value / $this->referenceIntBase);
  164.       }
  165.       $missingLength = $width -strlen($encoded);
  166.       for ($i = 0; $i < $missingLength; $i++) {
  167.         $encoded = chr($this->referenceIntFloorCode) . $encoded;
  168.       }
  169.       return $encoded;
  170.     } else {
  171.       throw new Exception('Reference int out of range: ' + $value + ' (width = ' + $width + ')');
  172.     }
  173.   }
  174.   private function encodeReferenceLength($length) {
  175.     return $this->encodeReferenceInt($length - $this->minStringLength, 1);
  176.   }
  177.   private function decodeReferenceInt($data, $width) {
  178.     $value = 0;
  179.     for ($i = 0; $i < $width; $i++) {
  180.       $value *= $this->referenceIntBase;
  181.       $charCode = ord(substr($data, $i, 1));
  182.       if (($charCode >= $this->referenceIntFloorCode) && ($charCode <= $this->referenceIntCeilCode)) {
  183.         $value += $charCode - $this->referenceIntFloorCode;
  184.       } else {
  185.         throw new Exception('Invalid char code in reference int: ' + $charCode);
  186.       }
  187.     }
  188.     return $value;
  189.   }
  190.   private function decodeReferenceLength($data) {
  191.     return $this->decodeReferenceInt($data, 1) + $this->minStringLength;
  192.   }
  193. }
Advertisement
Add Comment
Please, Sign In to add comment