Advertisement
giovani-rubim

huffman

Mar 16th, 2020
196
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. {
  2.  
  3.     // Chars '+' and '/' replaced by '-' and '_' to avoid the increasing of the encoded text when
  4.     // encoded to URI component.
  5.     const intToBase64 = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  6.         + 'abcdefghijklmnopqrstuvwxyz'
  7.         + '0123456789-_').split('');
  8.     const base64ToInt = {};
  9.  
  10.     const bin6LEToBase64 = {};
  11.     const base64ToBin6LE = {};
  12.     intToBase64.forEach((char, value) => {
  13.         base64ToInt[char] = value;
  14.         let bin = value.toString(2);
  15.         bin = '0'.repeat(6 - bin.length) + bin;
  16.         bin = bin.split('');
  17.         bin.reverse();
  18.         bin = bin.join('');
  19.         bin6LEToBase64[bin] = char;
  20.         base64ToBin6LE[char] = bin;
  21.     });
  22.  
  23.     const bytesToBase64 = bytes => {
  24.         let res = '';
  25.         const {length} = bytes;
  26.         for (let i=3; i<=length; i+=3) {
  27.             const a = bytes[i - 3];
  28.             const b = bytes[i - 2];
  29.             const c = bytes[i - 1];
  30.             const int24 = a | (b << 8) | (c << 16);
  31.             res += intToBase64[int24 & 63];
  32.             res += intToBase64[(int24 >> 6) & 63];
  33.             res += intToBase64[(int24 >> 12) & 63];
  34.             res += intToBase64[(int24 >> 18) & 63];
  35.         }
  36.         const left = length % 3;
  37.         let int = 0;
  38.         for (let i=length-left, shift=0; i<length; ++i, shift+=8) {
  39.             int |= bytes[i] << shift;
  40.         }
  41.         const nBits = left*8;
  42.         for (let i=0; i<nBits; i+=6) {
  43.             res += intToBase64[(int >> i) & 63];
  44.         }
  45.         return res;
  46.     };
  47.  
  48.     const base64ToBytes = (base64, bytes) => {
  49.         if (bytes === undefined) bytes = [];
  50.         const {length} = base64;
  51.         let j = 0;
  52.         for (let i=4; i<=length; i+=4) {
  53.             const a = base64ToInt[base64.charAt(i - 4)];
  54.             const b = base64ToInt[base64.charAt(i - 3)];
  55.             const c = base64ToInt[base64.charAt(i - 2)];
  56.             const d = base64ToInt[base64.charAt(i - 1)];
  57.             const int24 = a | (b << 6) | (c << 12) | (d << 18);
  58.             bytes[j++] = int24 & 0xff;
  59.             bytes[j++] = (int24 >> 8) & 0xff;
  60.             bytes[j++] = (int24 >> 16) & 0xff;
  61.         }
  62.         const left = length%4;
  63.         let int = 0;
  64.         for (let i=length-left, shift=0; i<length; ++i, shift+=6) {
  65.             int |= base64ToInt[base64.charAt(i)] << shift;
  66.         }
  67.         const nBits = left*6;
  68.         for (let i=8; i<=nBits; i+=8) {
  69.             bytes[j++] = (int >> (i - 8)) & 0xff;
  70.         }
  71.         return bytes;
  72.     };
  73.  
  74.     const binaryStringToBase64 = string => {
  75.         const {length} = string;
  76.         let res = '';
  77.         for (let i=6; i<=length; i+=6) {
  78.             const bin = string.substr(i - 6, 6);
  79.             res += bin6LEToBase64[bin];
  80.         }
  81.         const left = string.substr(length - length%6);
  82.         if (left !== '') {
  83.             res += bin6LEToBase64[left + '0'.repeat(6 - left.length)];
  84.         }
  85.         return res;
  86.     };
  87.  
  88.     const base64ToBinaryString = base64 => {
  89.         const {length} = base64;
  90.         let res = '';
  91.         for (let i=0; i<length; ++i) {
  92.             res += base64ToBin6LE[base64.charAt(i)];
  93.         }
  94.         return res;
  95.     };
  96.    
  97.     const stringToUtf8Bytes = (str, bytes = []) => {
  98.         const n = str.length;
  99.         let j = 0;
  100.         for (let i=0; i<n; ++i) {
  101.             const code = str.charCodeAt(i);
  102.             if (code < (1 << 7)) {
  103.                 bytes[j++] = code;
  104.             } else if (code < (1 << 11)) {
  105.                 bytes[j++] = 0b11000000 | (code >> 6);
  106.                 bytes[j++] = 0b10000000 | code & 0b00111111;
  107.             } else {
  108.                 bytes[j++] = 0b11100000 | (code >> 12);
  109.                 bytes[j++] = 0b10000000 | (code >> 6) & 0b00111111;
  110.                 bytes[j++] = 0b10000000 | code & 0b00111111;
  111.             }
  112.         }
  113.         return bytes;
  114.     };
  115.  
  116.     const utf8BytesToString = bytes => {
  117.         let res = '';
  118.         const n = bytes.length;
  119.         for (let i=0; i<n;) {
  120.             let byte = bytes[i++];
  121.             if ((byte & 0b10000000) === 0b10000000) {
  122.                 if ((byte & 0b11100000) === 0b11000000) {
  123.                     byte = (byte & 0b00011111) << 6;
  124.                     byte |= bytes[i++] & 0b00111111;
  125.                 } else {
  126.                     byte = (byte & 0b00001111) << 12;
  127.                     byte |= (bytes[i++] & 0b00111111) << 6;
  128.                     byte |= bytes[i++] & 0b00111111;
  129.                 }
  130.             }
  131.             res += String.fromCharCode(byte);
  132.         }
  133.         return res;
  134.     };
  135.  
  136.     const countFrequency = array => {
  137.         const map = {};
  138.         const nodes = [];
  139.         const {length} = array;
  140.         const string = typeof array === 'string';
  141.         for (let i=length; i!==0;) {
  142.             const value = string? array.charCodeAt(--i): array[--i];
  143.             const node = map[value];
  144.             if (node !== undefined) {
  145.                 ++ node.n;
  146.             } else {
  147.                 const node = {n: 1, value};
  148.                 map[value] = node;
  149.                 nodes.push(node);
  150.             }
  151.         }
  152.         return nodes;
  153.     };
  154.  
  155.     const heapMoveUp = (heap, node, index) => {
  156.         const {n} = node;
  157.         while (index !== 0) {
  158.             const parent_index = (index - 1) >> 1;
  159.             const parent = heap[parent_index];
  160.             if (parent.n <= n) break;
  161.             heap[index] = parent;
  162.             index = parent_index;
  163.         }
  164.         heap[index] = node;
  165.     };
  166.  
  167.     const fixHeap = heap => {
  168.         const {length} = heap;
  169.         for (let i=1; i<length; ++i) {
  170.             heapMoveUp(heap, heap[i], i);
  171.         }
  172.     };
  173.  
  174.     const heapMoveDown = (heap, node, index, length) => {
  175.         const {n} = node;
  176.         for (;;) {
  177.             const i0 = (index << 1)|1;
  178.             if (i0 >= length) break;
  179.             const i1 = i0 + 1;
  180.             if (i1 >= length) {
  181.                 const child = heap[i0];
  182.                 if (child.n >= n) break;
  183.                 heap[index] = child;
  184.                 index = i0;
  185.                 break;
  186.             }
  187.             const c0 = heap[i0];
  188.             const c1 = heap[i1];
  189.             const n0 = c0.n;
  190.             const n1 = c1.n;
  191.             if (n0 <= n1) {
  192.                 if (n0 >= n) break;
  193.                 heap[index] = c0;
  194.                 index = i0;
  195.                 continue;
  196.             }
  197.             if (n1 >= n) break;
  198.             heap[index] = c1;
  199.             index = i1;
  200.         }
  201.         heap[index] = node;
  202.     };
  203.  
  204.     const heapPop = (heap, length) => {
  205.         const res = heap[0];
  206.         const node = heap[--length];
  207.         heapMoveDown(heap, node, 0, length);
  208.         return res;
  209.     };
  210.  
  211.     const heapPush = (heap, node, length) => {
  212.         heap[length] = node;
  213.         heapMoveUp(heap, node, length);
  214.     };
  215.  
  216.     const createTree = heap => {
  217.         let {length} = heap;
  218.         if (length === 0) {
  219.             heap.push({value: 0, n: 0}, {value: 1, n: 0});
  220.             length = 2;
  221.         } else if (length === 1) {
  222.             const node = heap[0];
  223.             heap.push({value: node.value^1, n: 0});
  224.             length = 2;
  225.         }
  226.         while (length > 1) {
  227.             const a = heapPop(heap, length--);
  228.             const b = heapPop(heap, length--);
  229.             const node = {a, b, n: a.n + b.n};
  230.             heapPush(heap, node, length++);
  231.         }
  232.         return heap[0];
  233.     };
  234.  
  235.     const stringifyTree = ({value, a, b}) => {
  236.         if (a == null) {
  237.             return value;
  238.         }
  239.         return `{${stringifyTree(a)};${stringifyTree(b)}}`;
  240.     };
  241.  
  242.     const encodeTree = tree => {
  243.         const levels = [];
  244.         let allChars = '';
  245.         const addNode = (node, level) => {
  246.             const {value, a, b} = node;
  247.             levels[level] = (levels[level] || '') + ((value !== undefined)|0);
  248.             if (value === undefined) {
  249.                 addNode(a, level + 1);
  250.                 addNode(b, level + 1);
  251.             } else {
  252.                 allChars += String.fromCharCode(value);
  253.             }
  254.         };
  255.         addNode(tree, 0);
  256.         const structure = binaryStringToBase64(levels.join('').substr(1));
  257.         const chars = bytesToBase64(stringToUtf8Bytes(allChars));
  258.         return `${structure}.${chars}`;
  259.     };
  260.  
  261.     const mapTreeForward = tree => {
  262.         const map = {};
  263.         const mapNode = ({a, b, value}, bin) => {
  264.             if (value === undefined) {
  265.                 mapNode(a, bin + '0');
  266.                 mapNode(b, bin + '1');
  267.             } else {
  268.                 map[value] = bin;
  269.             }
  270.         };
  271.         mapNode(tree, '');
  272.         return map;
  273.     };
  274.  
  275.     const mapTreeBackward = tree => {
  276.         const map = {};
  277.         const mapNode = ({a, b, value}, bin) => {
  278.             if (value === undefined) {
  279.                 mapNode(a, bin + '0');
  280.                 mapNode(b, bin + '1');
  281.             } else {
  282.                 map[bin] = String.fromCharCode(value);
  283.             }
  284.         };
  285.         mapNode(tree, '');
  286.         return map;
  287.     };
  288.  
  289.     const decodeTree = string => {
  290.         const [structure, chars] = string.split('.');
  291.         const bin = base64ToBinaryString(structure);
  292.         const nBits = bin.length;
  293.         const root = {a: null, b: null};
  294.         const queue = [root];
  295.         for (let i=0; i<nBits; ++i) {
  296.             let node = {a: null, b: null};
  297.             let parent = queue[0];
  298.             if (parent.a === null) {
  299.                 parent.a = node;
  300.             } else {
  301.                 parent.b = node;
  302.                 queue.splice(0, 1);
  303.             }
  304.             const bit = bin.charAt(i);
  305.             if (bit === '0') {
  306.                 queue.push(node);
  307.             } else if (bit !== '1') {
  308.                 throw 'Invalid huffman tree';
  309.             }
  310.             if (queue.length === 0) {
  311.                 break;
  312.             }
  313.         }
  314.         const allChars = utf8BytesToString(base64ToBytes(chars));
  315.         let index = 0;
  316.         const addValue = node => {
  317.             const {a, b} = node;
  318.             if (a) {
  319.                 addValue(a);
  320.                 addValue(b);
  321.             } else {
  322.                 const value = allChars.charCodeAt(index++);
  323.                 if (isNaN(value)) {
  324.                     throw 'Incomplete character list';
  325.                 }
  326.                 node.value = value;
  327.             }
  328.         };
  329.         addValue(root);
  330.         return root;
  331.     };
  332.  
  333.     const encodeSize = size => {
  334.         return bytesToBase64([size&0xff, (size >> 8)&0xff, (size >> 16)&0xff]);
  335.     };
  336.  
  337.     const decodeSize = base64 => {
  338.         const [a, b, c] = base64ToBytes(base64);
  339.         return a | (b << 8) | (c << 8);
  340.     };
  341.  
  342.     const encodeText = (text, map) => {
  343.         const {length} = text;
  344.         let res = '';
  345.         let buffer = '';
  346.         for (let i=0; i<length;) {
  347.             for (let j=0; i<length && j<100; ++i, ++j) {
  348.                 buffer += map[text.charCodeAt(i)];
  349.             }
  350.             const bLen = buffer.length;
  351.             const left = bLen%6;
  352.             const flushed = bLen - left;
  353.             res += binaryStringToBase64(buffer.substr(0, flushed));
  354.             buffer = buffer.substr(flushed);
  355.         }
  356.         res += binaryStringToBase64(buffer);
  357.         return encodeSize(length) + '.' + res;
  358.     };
  359.  
  360.     const decodeText = (base64, textSize, map) => {
  361.         const {length} = base64;
  362.         let buffer = '';
  363.         let text = '', i;
  364.         for (i=0; i < length && text.length < textSize;) {
  365.             const size = Math.min(length - i, 100);
  366.             buffer += base64ToBinaryString(base64.substr(i, size));
  367.             i += size;
  368.             const bLen = buffer.length;
  369.             let n = 0;
  370.             let bin = '';
  371.             for (let j=0; j<bLen; ++j) {
  372.                 bin += buffer.charAt(j);
  373.                 const value = map[bin];
  374.                 if (value !== undefined) {
  375.                     text += value;
  376.                     if (text.length >= textSize) break;
  377.                     n += bin.length;
  378.                     bin = '';
  379.                 }
  380.             }
  381.             buffer = buffer.substr(n);
  382.         }
  383.         if (i !== length || text.length !== textSize) {
  384.             throw 'Invalid content';
  385.         }
  386.         return text;
  387.     };
  388.  
  389.     const compress = text => {
  390.         const heap = countFrequency(text);
  391.         fixHeap(heap);
  392.         const tree = createTree(heap);
  393.         const forwardMap = mapTreeForward(tree);
  394.         const head = encodeTree(tree);
  395.         const body = encodeText(text, forwardMap);
  396.         return head + '.' + body;
  397.     };
  398.  
  399.     const decompress = compressed => {
  400.         compressed = compressed.split('.');
  401.         if (compressed.length !== 4) {
  402.             throw 'Invalid format';
  403.         }
  404.         const [a, b, c, d] = compressed;
  405.         const tree = decodeTree(a + '.' + b);
  406.         const backwardMap = mapTreeBackward(tree);
  407.         const size = decodeSize(c);
  408.         if (typeof size !== 'number' || isNaN(size)) {
  409.             throw 'Invalid size';
  410.         }
  411.         return decodeText(d, size, backwardMap);
  412.     };
  413.  
  414.     window.huffman = {compress, decompress};
  415.  
  416. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement