Advertisement
rg443

javascript lz77 compression

Jan 27th, 2013
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // This class provides simple LZ77 compression and decompression.
  2. // https://github.com/olle/lz77-kit
  3. // https://raw.github.com/olle/lz77-kit/master/src/main/js/lz77.js
  4.  
  5. /*
  6. compressor = new LZ77();
  7. var c= compressor.compress("abcacdacdcdacdacdadcadcda");
  8. compressor.decompress(c);
  9. */
  10.  
  11.  
  12. var LZ77 = function (settings) {
  13.  
  14.     settings = settings || {}; 
  15.    
  16.     // PRIVATE
  17.    
  18.     var referencePrefix = "`";
  19.     var referenceIntBase = settings.referenceIntBase || 96;
  20.     var referenceIntFloorCode = " ".charCodeAt(0);
  21.     var referenceIntCeilCode = referenceIntFloorCode + referenceIntBase - 1;
  22.     var maxStringDistance = Math.pow(referenceIntBase, 2) - 1;
  23.     var minStringLength = settings.minStringLength || 5;
  24.     var maxStringLength = Math.pow(referenceIntBase, 1) - 1 + minStringLength;
  25.     var defaultWindowLength = settings.defaultWindowLength || 144;
  26.     var maxWindowLength = maxStringDistance + minStringLength;
  27.    
  28.     var encodeReferenceInt = function (value, width) {
  29.         if ((value >= 0) && (value < (Math.pow(referenceIntBase, width) - 1))) {
  30.             var encoded = "";
  31.             while (value > 0) {
  32.                 encoded = (String.fromCharCode((value % referenceIntBase) + referenceIntFloorCode)) + encoded;
  33.                 value = Math.floor(value / referenceIntBase);
  34.             }
  35.             var missingLength = width - encoded.length;
  36.             for (var i = 0; i < missingLength; i++) {
  37.                 encoded = String.fromCharCode(referenceIntFloorCode) + encoded;
  38.             }
  39.             return encoded;
  40.         } else {
  41.             throw "Reference int out of range: " + value + " (width = " + width + ")";
  42.         }
  43.     };
  44.    
  45.     var encodeReferenceLength = function (length) {
  46.         return encodeReferenceInt(length - minStringLength, 1);
  47.     };
  48.    
  49.     var decodeReferenceInt = function (data, width) {
  50.         var value = 0;
  51.         for (var i = 0; i < width; i++) {
  52.             value *= referenceIntBase;
  53.             var charCode = data.charCodeAt(i);
  54.             if ((charCode >= referenceIntFloorCode) && (charCode <= referenceIntCeilCode)) {
  55.                 value += charCode - referenceIntFloorCode;
  56.             } else {
  57.                 throw "Invalid char code in reference int: " + charCode;
  58.             }
  59.         }
  60.         return value;
  61.     };
  62.    
  63.     var decodeReferenceLength = function (data) {
  64.         return decodeReferenceInt(data, 1) + minStringLength;
  65.     };
  66.        
  67.     // PUBLIC
  68.    
  69.     /**
  70.      * Compress data using the LZ77 algorithm.
  71.      *
  72.      * @param data
  73.      * @param windowLength
  74.      */
  75.     this.compress = function (data, windowLength) {
  76.         windowLength = windowLength || defaultWindowLength;
  77.         if (windowLength > maxWindowLength) {
  78.             throw "Window length too large";
  79.         }
  80.         var compressed = "";
  81.         var pos = 0;
  82.         var lastPos = data.length - minStringLength;
  83.         while (pos < lastPos) {
  84.             var searchStart = Math.max(pos - windowLength, 0);
  85.             var matchLength = minStringLength;
  86.             var foundMatch = false;
  87.             var bestMatch = {distance:maxStringDistance, length:0};
  88.             var newCompressed = null;
  89.             while ((searchStart + matchLength) < pos) {
  90.                 var isValidMatch = ((data.substr(searchStart, matchLength) == data.substr(pos, matchLength)) && (matchLength < maxStringLength));
  91.                 if (isValidMatch) {
  92.                     matchLength++;
  93.                     foundMatch = true;
  94.                 } else {
  95.                     var realMatchLength = matchLength - 1;
  96.                     if (foundMatch && (realMatchLength > bestMatch.length)) {
  97.                         bestMatch.distance = pos - searchStart - realMatchLength;
  98.                         bestMatch.length = realMatchLength;
  99.                     }
  100.                     matchLength = minStringLength;
  101.                     searchStart++;
  102.                     foundMatch = false;
  103.                 }
  104.             }
  105.             if (bestMatch.length) {
  106.                 newCompressed = referencePrefix + encodeReferenceInt(bestMatch.distance, 2) + encodeReferenceLength(bestMatch.length);
  107.                 pos += bestMatch.length;
  108.             } else {
  109.                 if (data.charAt(pos) != referencePrefix) {
  110.                     newCompressed = data.charAt(pos);
  111.                 } else {
  112.                     newCompressed = referencePrefix + referencePrefix;
  113.                 }
  114.                 pos++;
  115.             }
  116.             compressed += newCompressed;
  117.         }
  118.         return compressed + data.slice(pos).replace(/`/g, "``");
  119.     };
  120.    
  121.     /**
  122.      * Decompresses LZ77 compressed data.
  123.      *
  124.      * @param data
  125.      */
  126.     this.decompress = function (data) {
  127.         var decompressed = "";
  128.         var pos = 0;
  129.         while (pos < data.length) {
  130.             var currentChar = data.charAt(pos);
  131.             if (currentChar != referencePrefix) {
  132.                 decompressed += currentChar;
  133.                 pos++;
  134.             } else {
  135.                 var nextChar = data.charAt(pos + 1);
  136.                 if (nextChar != referencePrefix) {
  137.                     var distance = decodeReferenceInt(data.substr(pos + 1, 2), 2);
  138.                     var length = decodeReferenceLength(data.charAt(pos + 3));
  139.                     decompressed += decompressed.substr(decompressed.length - distance - length, length);
  140.                     pos += minStringLength - 1;
  141.                 } else {
  142.                     decompressed += referencePrefix;
  143.                     pos += 2;
  144.                 }
  145.             }
  146.         }
  147.         return decompressed;
  148.     };
  149. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement