huffman.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. *
  6. * ported from https://github.com/photopea/UZIP.js/blob/master/UZIP.js
  7. * MIT License, Copyright (c) 2018 Photopea
  8. */
  9. import { NumberArray } from '../type-helpers';
  10. export type HufTree = {
  11. lit: number,
  12. f: number,
  13. l?: HufTree,
  14. r?: HufTree,
  15. d: number
  16. }
  17. export function _hufTree(hst: NumberArray, tree: number[], MAXL: number) {
  18. const list: HufTree[] = [];
  19. const hl = hst.length, tl = tree.length;
  20. for(let i = 0; i < tl; i += 2) {
  21. tree[i] = 0;
  22. tree[i + 1] = 0;
  23. }
  24. for(let i = 0; i < hl; i++) if(hst[i] !== 0) list.push({ lit: i, f: hst[i], d: undefined as any });
  25. const end = list.length, l2 = list.slice(0);
  26. if(end === 0) return 0; // empty histogram (usually for dist)
  27. if(end === 1) {
  28. const lit = list[0].lit, l2 = lit === 0 ? 1 : 0;
  29. tree[(lit << 1) + 1] = 1;
  30. tree[(l2 << 1) + 1] = 1;
  31. return 1;
  32. }
  33. list.sort(function(a, b){return a.f - b.f;});
  34. let a = list[0], b = list[1], i0 = 0, i1 = 1, i2 = 2;
  35. list[0] = {
  36. lit: -1,
  37. f: a.f + b.f,
  38. l: a,
  39. r: b,
  40. d: 0
  41. };
  42. while(i1 !== end - 1) {
  43. if(i0 !== i1 && (i2 === end || list[i0].f < list[i2].f)) {
  44. a = list[i0++];
  45. } else {
  46. a = list[i2++];
  47. }
  48. if(i0 !== i1 && (i2 === end || list[i0].f < list[i2].f)) {
  49. b = list[i0++];
  50. } else {
  51. b = list[i2++];
  52. }
  53. list[i1++] = {
  54. lit: -1,
  55. f: a.f + b.f,
  56. l: a,
  57. r: b,
  58. d: undefined as any
  59. };
  60. }
  61. let maxl = setDepth(list[i1 - 1], 0);
  62. if(maxl > MAXL) {
  63. restrictDepth(l2, MAXL, maxl);
  64. maxl = MAXL;
  65. }
  66. for(let i = 0; i < end; i++) tree[(l2[i].lit << 1) + 1] = l2[i].d;
  67. return maxl;
  68. }
  69. function setDepth(t: HufTree, d: number): number {
  70. if(t.lit !== -1) {
  71. t.d = d;
  72. return d;
  73. }
  74. return Math.max(setDepth(t.l!, d + 1), setDepth(t.r!, d + 1));
  75. }
  76. function restrictDepth(dps: HufTree[], MD: number, maxl: number) {
  77. let i = 0, bCost = 1 << (maxl - MD), dbt = 0;
  78. dps.sort(function(a: HufTree, b: HufTree){return b.d === a.d ? a.f - b.f : b.d - a.d;});
  79. for(i = 0; i < dps.length; i++) {
  80. if(dps[i].d > MD) {
  81. const od = dps[i].d;
  82. dps[i].d = MD;
  83. dbt += bCost - (1 << (maxl - od));
  84. } else {
  85. break;
  86. }
  87. }
  88. dbt = dbt >>> (maxl - MD);
  89. while(dbt > 0) {
  90. const od = dps[i].d;
  91. if(od < MD) {
  92. dps[i].d++;
  93. dbt -= (1 << (MD - od - 1));
  94. } else {
  95. i++;
  96. }
  97. }
  98. for(; i >= 0; i--) {
  99. if(dps[i].d === MD && dbt < 0) {
  100. dps[i].d--;
  101. dbt++;
  102. }
  103. }
  104. if(dbt !== 0) console.log('debt left');
  105. }