lzw.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import BaseDecoder from './basedecoder.js';
  2. const MIN_BITS = 9;
  3. const CLEAR_CODE = 256; // clear code
  4. const EOI_CODE = 257; // end of information
  5. const MAX_BYTELENGTH = 12;
  6. function getByte(array, position, length) {
  7. const d = position % 8;
  8. const a = Math.floor(position / 8);
  9. const de = 8 - d;
  10. const ef = (position + length) - ((a + 1) * 8);
  11. let fg = (8 * (a + 2)) - (position + length);
  12. const dg = ((a + 2) * 8) - position;
  13. fg = Math.max(0, fg);
  14. if (a >= array.length) {
  15. console.warn('ran off the end of the buffer before finding EOI_CODE (end on input code)');
  16. return EOI_CODE;
  17. }
  18. let chunk1 = array[a] & ((2 ** (8 - d)) - 1);
  19. chunk1 <<= (length - de);
  20. let chunks = chunk1;
  21. if (a + 1 < array.length) {
  22. let chunk2 = array[a + 1] >>> fg;
  23. chunk2 <<= Math.max(0, (length - dg));
  24. chunks += chunk2;
  25. }
  26. if (ef > 8 && a + 2 < array.length) {
  27. const hi = ((a + 3) * 8) - (position + length);
  28. const chunk3 = array[a + 2] >>> hi;
  29. chunks += chunk3;
  30. }
  31. return chunks;
  32. }
  33. function appendReversed(dest, source) {
  34. for (let i = source.length - 1; i >= 0; i--) {
  35. dest.push(source[i]);
  36. }
  37. return dest;
  38. }
  39. function decompress(input) {
  40. const dictionaryIndex = new Uint16Array(4093);
  41. const dictionaryChar = new Uint8Array(4093);
  42. for (let i = 0; i <= 257; i++) {
  43. dictionaryIndex[i] = 4096;
  44. dictionaryChar[i] = i;
  45. }
  46. let dictionaryLength = 258;
  47. let byteLength = MIN_BITS;
  48. let position = 0;
  49. function initDictionary() {
  50. dictionaryLength = 258;
  51. byteLength = MIN_BITS;
  52. }
  53. function getNext(array) {
  54. const byte = getByte(array, position, byteLength);
  55. position += byteLength;
  56. return byte;
  57. }
  58. function addToDictionary(i, c) {
  59. dictionaryChar[dictionaryLength] = c;
  60. dictionaryIndex[dictionaryLength] = i;
  61. dictionaryLength++;
  62. return dictionaryLength - 1;
  63. }
  64. function getDictionaryReversed(n) {
  65. const rev = [];
  66. for (let i = n; i !== 4096; i = dictionaryIndex[i]) {
  67. rev.push(dictionaryChar[i]);
  68. }
  69. return rev;
  70. }
  71. const result = [];
  72. initDictionary();
  73. const array = new Uint8Array(input);
  74. let code = getNext(array);
  75. let oldCode;
  76. while (code !== EOI_CODE) {
  77. if (code === CLEAR_CODE) {
  78. initDictionary();
  79. code = getNext(array);
  80. while (code === CLEAR_CODE) {
  81. code = getNext(array);
  82. }
  83. if (code === EOI_CODE) {
  84. break;
  85. } else if (code > CLEAR_CODE) {
  86. throw new Error(`corrupted code at scanline ${code}`);
  87. } else {
  88. const val = getDictionaryReversed(code);
  89. appendReversed(result, val);
  90. oldCode = code;
  91. }
  92. } else if (code < dictionaryLength) {
  93. const val = getDictionaryReversed(code);
  94. appendReversed(result, val);
  95. addToDictionary(oldCode, val[val.length - 1]);
  96. oldCode = code;
  97. } else {
  98. const oldVal = getDictionaryReversed(oldCode);
  99. if (!oldVal) {
  100. throw new Error(`Bogus entry. Not in dictionary, ${oldCode} / ${dictionaryLength}, position: ${position}`);
  101. }
  102. appendReversed(result, oldVal);
  103. result.push(oldVal[oldVal.length - 1]);
  104. addToDictionary(oldCode, oldVal[oldVal.length - 1]);
  105. oldCode = code;
  106. }
  107. if (dictionaryLength + 1 >= (2 ** byteLength)) {
  108. if (byteLength === MAX_BYTELENGTH) {
  109. oldCode = undefined;
  110. } else {
  111. byteLength++;
  112. }
  113. }
  114. code = getNext(array);
  115. }
  116. return new Uint8Array(result);
  117. }
  118. export default class LZWDecoder extends BaseDecoder {
  119. decodeBlock(buffer) {
  120. return decompress(buffer, false).buffer;
  121. }
  122. }