Arjun Barrett 4 éve
commit
81fba55bcd
9 módosított fájl, 547 hozzáadás és 0 törlés
  1. 3 0
      .gitignore
  2. 4 0
      .npmignore
  3. 3 0
      README.md
  4. 248 0
      index.ts
  5. 19 0
      package.json
  6. 247 0
      src/flate.ts
  7. 8 0
      tsconfig.esm.json
  8. 7 0
      tsconfig.json
  9. 8 0
      yarn.lock

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+node_modules/
+lib/
+esm/

+ 4 - 0
.npmignore

@@ -0,0 +1,4 @@
+*
+!lib/
+!package.json
+!README.md

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+# fflate
+Native performance (de)compression in a 5 kB package
+

+ 248 - 0
index.ts

@@ -0,0 +1,248 @@
+const u8 = Uint8Array, u16 = Uint16Array;
+
+// fixed lengths
+const fl = new u16([3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, /* unused */ 258, 258, /* impossible */ 258]);
+
+// fixed length extra bits
+// yes, this can be calculated, but hardcoding is more efficient
+const fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
+
+// fixed distances
+const fd = new u16([1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, /* unused */ 32768, 32768]);
+
+// fixed distance extra bits
+// see fleb note
+const fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 13, 13]);
+
+// code length index map
+const clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
+
+// map of value to reverse (assuming 16 bits)
+const rev = new u16(32768);
+for (let i = 0; i < 32768; ++i) {
+  // reverse table algorithm from UZIP.js
+  let x = i;
+  x = ((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1);
+  x = ((x & 0xcccccccc) >>> 2) | ((x & 0x33333333) << 2);
+  x = ((x & 0xf0f0f0f0) >>> 4) | ((x & 0x0f0f0f0f) << 4);
+  rev[i] = ((x & 0xff00ff00) >>> 8) | ((x & 0x00ff00ff) << 8);
+}
+
+// create huffman tree from u8 "map": index -> code length for code index
+// mb (max bits) must be at most 15
+const hTree = (cd: Uint8Array, mb: number) => {
+  // index
+  let i = 0;
+  // u8 "map": index -> # of codes with bit length = index
+  const l = new u8(mb);
+  // length of cd must be 288 (total # of codes)
+  for (; i < cd.length; ++i) ++l[cd[i] - 1];
+  // u16 "map": index -> minimum code for bit length = index
+  const le = new u16(mb);
+  for (i = 0; i < mb; ++i) {
+    le[i] = (le[i - 1] + l[i - 1]) << 1;
+  }
+  // u16 "map": index -> number of actual bits, symbol for code
+  const co = new u16(1 << mb);
+  for (i = 0; i < cd.length; ++i) {
+    // ignore 0 lengths
+    if (cd[i]) {
+      // num encoding both symbol and bits read
+      const sv = (i << 4) | cd[i];
+      // free bits
+      const r = mb - cd[i];
+      // bits to remove for reverser
+      const rvb = 16 - mb;
+      // start value
+      let v = le[cd[i] - 1]++ << r;
+      // m is end value
+      for (const m = v | ((1 << r) - 1); v <= m; ++v) {
+        // every 16 bit value starting with the code yields the same result
+        co[rev[v] >>> rvb] = sv;
+      }
+    }
+  }
+  return co;
+}
+
+// fixed length tree
+const flt = new u8(288);
+for (let i = 0; i < 144; ++i) flt[i] = 8;
+for (let i = 144; i < 256; ++i) flt[i] = 9;
+for (let i = 256; i < 280; ++i) flt[i] = 7;
+for (let i = 280; i < 288; ++i) flt[i] = 8;
+// fixed distance tree
+const fdt = new u8(32);
+for (let i = 0; i < 32; ++i) fdt[i] = 5;
+// fixed length map
+const flm = hTree(flt, 9);
+// fixed distance map
+const fdm = hTree(fdt, 5);
+// fixed length mask
+const fml = (1 << 9) - 1;
+// fixed dist mask
+const fmd = (1 << 5) - 1;
+
+// find max of array
+const max = (a: Uint8Array) => {
+  let m = a[0];
+  for (let i = 0; i < a.length; ++i) {
+    if (a[i] > m) m = a[i];
+  }
+  return m;
+};
+
+// read d, starting at bit p continuing for l bits
+const bits = (d: Uint8Array, p: number, l: number) => {
+  const o = p >>> 3;
+  return ((d[o] | (d[o + 1] << 8)) >>> (p & 7)) & ((1 << l) - 1);
+}
+
+// read d, starting at bit p continuing for at least 16 bits
+const bits16 = (d: Uint8Array, p: number) => {
+  const o = p >>> 3;
+  return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16) | (d[o + 3] << 24)) >>> (p & 7));
+}
+
+// maximum chunk size (practically, theoretically infinite)
+const MC = 1 << 17;
+
+
+const inflate = (dat: Uint8Array, buf?: Uint8Array) => {
+  // have to estimate size
+  const noBuf = buf == null;
+  // Slightly less than 2x - assumes ~60% compression ratio
+  if (noBuf) buf = new u8((dat.length >>> 2) << 3);
+  // ensure buffer can fit at least l elements
+  const cbuf = (l: number) => {
+    let bl = buf.length;
+    // need to increase size to fit
+    if (l > bl) {
+      // Double or set to necessary, whichever is greater
+      const nbuf = new u8(Math.max(bl << 1, l));
+      nbuf.set(buf);
+      buf = nbuf;
+    }
+  }
+  //  last chunk     chunktype literal   dist       lengths    lmask   dmask
+  let final = false, type = 0, hLit = 0, hDist = 0, hcLen = 0, ml = 0, md = 0;
+  //  bitpos   bytes
+  let pos = 0, bt = 0;
+  //  len                dist
+  let lm: Uint16Array, dm: Uint16Array;
+  while (!final) {
+    // BFINAL - this is only 1 when last chunk is next
+    final = bits(dat, pos, 1) as unknown as boolean;
+    // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
+    type = bits(dat, pos + 1, 2);
+    pos += 3;
+    if (!type) {
+      // go to end of byte boundary
+      if (pos & 7) pos += 8 - (pos & 7);
+      const s = (pos >>> 3) + 4, l = dat[s - 4] | (dat[s - 3] << 8);
+      // ensure size
+      if (noBuf) cbuf(bt + l);
+      // Copy over uncompressed data
+      buf.set(dat.subarray(s, s + l), bt);
+      // Get new bitpos, update byte count
+      pos = (s + l) << 3, bt += l;
+      continue;
+    }
+    // Make sure the buffer can hold this + the largest possible addition
+    if (noBuf) cbuf(bt + MC);
+    if (type == 1) {
+      lm = flm;
+      dm = fdm;
+      ml = fml;
+      md = fmd;
+    }
+    else if (type == 2) {
+      hLit = bits(dat, pos, 5) + 257;
+      hDist = bits(dat, pos + 5, 5) + 1;
+      hcLen = bits(dat, pos + 10, 4) + 4;
+      pos += 14;
+      // length+distance tree
+      const ldt = new u8(hLit + hDist);
+      // code length tree
+      const clt = new u8(19);
+      for (let i = 0; i < hcLen; ++i) {
+        // use index map to get real code
+        clt[clim[i]] = bits(dat, pos + i * 3, 3);
+      }
+      pos += hcLen * 3;
+      // code lengths bits
+      const clb = max(clt);
+      // code lengths map
+      const clm = hTree(clt, clb);
+      for (let i = 0; i < ldt.length;) {
+        const r = clm[bits(dat, pos, clb)];
+        // bits read
+        pos += r & 15;
+        // symbol
+        const s = r >>> 4;
+        // code length to copy
+        if (s < 16) {
+          ldt[i++] = s;
+        } else {
+          //  copy   count
+          let c = 0, n = 0;
+          if (s == 16) n = 3 + bits(dat, pos, 2), pos += 2, c = ldt[i - 1];
+          else if (s == 17) n = 3 + bits(dat, pos, 3), pos += 3;
+          else if (s == 18) n = 11 + bits(dat, pos, 7), pos += 7;
+          while (n--) ldt[i++] = c;
+        }
+      }
+      //    length tree                 distance tree
+      const lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
+      // max length bits
+      const mlb = max(lt)
+      // max dist bits
+      const mdb = max(dt);
+      ml = (1 << mlb) - 1;
+      lm = hTree(lt, mlb);
+      md = (1 << mdb) - 1;
+      dm = hTree(dt, mdb);
+    }
+    while (1) {
+      // bits read, code
+      const c = lm[bits16(dat, pos) & ml];
+      pos += c & 15;
+      // code
+      const sym = c >>> 4;
+      if (sym < 256) buf[bt++] = sym;
+      else if (sym == 256) break;
+      else {
+        let end = bt + sym - 254;
+        // no extra bits needed if less
+        if (sym > 264) {
+          // index
+          const i = sym - 257;
+          end = bt + bits(dat, pos, fleb[i]) + fl[i];
+          pos += fleb[i];
+        }
+        // dist
+        const d = dm[bits16(dat, pos) & md];
+        pos += d & 15;
+        const dsym = d >>> 4;
+        let dt = fd[dsym];
+        if (dsym > 3) {
+          dt += bits16(dat, pos) & ((1 << fdeb[dsym]) - 1);
+          pos += fdeb[dsym];
+        }
+        if (noBuf) cbuf(bt + MC);
+        while (bt < end) {
+          buf[bt] = buf[bt++-dt];
+          buf[bt] = buf[bt++-dt];
+          buf[bt] = buf[bt++-dt];
+          buf[bt] = buf[bt++-dt];
+        }
+        bt = end;
+      }
+    }
+  }
+  return buf.slice(0, bt);
+}
+
+
+
+export { inflate };

+ 19 - 0
package.json

@@ -0,0 +1,19 @@
+{
+  "name": "fflate",
+  "version": "0.0.1",
+  "description": "Native performance decompression in a 3 kB package",
+  "main": "lib/index.js",
+  "module": "esm/index.js",
+  "types": "lib/index.d.ts",
+  "repository": "https://github.com/101arrowz/fflate",
+  "author": "Arjun Barrett",
+  "license": "MIT",
+  "scripts": {
+    "build": "tsc && tsc --project tsconfig.esm.json",
+    "test": "node test.js",
+    "prepublish": "yarn build"
+  },
+  "devDependencies": {
+    "typescript": "^4.0.2"
+  }
+}

+ 247 - 0
src/flate.ts

@@ -0,0 +1,247 @@
+const u8 = Uint8Array, u16 = Uint16Array;
+
+// fixed lengths
+const fl = new u16([3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, /* unused */ 258, 258, /* impossible */ 258]);
+
+// fixed length extra bits
+// yes, this can be calculated, but hardcoding is more efficient
+const fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
+
+// fixed distances
+const fd = new u16([1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, /* unused */ 32768, 32768]);
+
+// fixed distance extra bits
+// see fleb note
+const fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 13, 13]);
+
+// code length index map
+const clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
+
+// map of value to reverse (assuming 16 bits)
+const rev = new u16(32768);
+for (let i = 0; i < 32768; ++i) {
+  // reverse table algorithm from UZIP.js
+  let x = i;
+  x = ((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1);
+  x = ((x & 0xcccccccc) >>> 2) | ((x & 0x33333333) << 2);
+  x = ((x & 0xf0f0f0f0) >>> 4) | ((x & 0x0f0f0f0f) << 4);
+  rev[i] = ((x & 0xff00ff00) >>> 8) | ((x & 0x00ff00ff) << 8);
+}
+
+// create huffman tree from u8 "map": index -> code length for code index
+// mb (max bits) must be at most 15
+const hTree = (cd: Uint8Array, mb: number) => {
+  // index
+  let i = 0;
+  // u8 "map": index -> # of codes with bit length = index
+  const l = new u8(mb);
+  // length of cd must be 288 (total # of codes)
+  for (; i < cd.length; ++i) ++l[cd[i] - 1];
+  // u16 "map": index -> minimum code for bit length = index
+  const le = new u16(mb);
+  for (i = 0; i < mb; ++i) {
+    le[i] = (le[i - 1] + l[i - 1]) << 1;
+  }
+  // u16 "map": index -> number of actual bits, symbol for code
+  const co = new u16(1 << mb);
+  for (i = 0; i < cd.length; ++i) {
+    // ignore 0 lengths
+    if (cd[i]) {
+      // num encoding both symbol and bits read
+      const sv = (i << 4) | cd[i];
+      // free bits
+      const r = mb - cd[i];
+      // bits to remove for reverser
+      const rvb = 16 - mb;
+      // start value
+      let v = le[cd[i] - 1]++ << r;
+      // m is end value
+      for (const m = v | ((1 << r) - 1); v <= m; ++v) {
+        // every 16 bit value starting with the code yields the same result
+        co[rev[v] >>> rvb] = sv;
+      }
+    }
+  }
+  return co;
+}
+
+// fixed length tree
+const flt = new u8(288);
+for (let i = 0; i < 144; ++i) flt[i] = 8;
+for (let i = 144; i < 256; ++i) flt[i] = 9;
+for (let i = 256; i < 280; ++i) flt[i] = 7;
+for (let i = 280; i < 288; ++i) flt[i] = 8;
+// fixed distance tree
+const fdt = new u8(32);
+for (let i = 0; i < 32; ++i) fdt[i] = 5;
+// fixed length map
+const flm = hTree(flt, 9);
+// fixed distance map
+const fdm = hTree(fdt, 5);
+// fixed length mask
+const fml = (1 << 9) - 1;
+// fixed dist mask
+const fmd = (1 << 5) - 1;
+
+// find max of array
+const max = (a: Uint8Array) => {
+  let m = a[0];
+  for (let i = 0; i < a.length; ++i) {
+    if (a[i] > m) m = a[i];
+  }
+  return m;
+};
+
+// read d, starting at bit p continuing for l bits
+const bits = (d: Uint8Array, p: number, l: number) => {
+  const o = p >>> 3;
+  return ((d[o] | (d[o + 1] << 8)) >>> (p & 7)) & ((1 << l) - 1);
+}
+
+// read d, starting at bit p continuing for at least 16 bits
+const bits16 = (d: Uint8Array, p: number) => {
+  const o = p >>> 3;
+  return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16) | (d[o + 3] << 24)) >>> (p & 7));
+}
+
+// maximum chunk size (practically, theoretically infinite)
+const MC = 1 << 17;
+
+// expands raw DEFLATE data
+const inflate = (dat: Uint8Array, buf?: Uint8Array) => {
+  // have to estimate size
+  const noBuf = buf == null;
+  // Slightly less than 2x - assumes ~60% compression ratio
+  if (noBuf) buf = new u8((dat.length >>> 2) << 3);
+  // ensure buffer can fit at least l elements
+  const cbuf = (l: number) => {
+    let bl = buf.length;
+    // need to increase size to fit
+    if (l > bl) {
+      // Double or set to necessary, whichever is greater
+      const nbuf = new u8(Math.max(bl << 1, l));
+      nbuf.set(buf);
+      buf = nbuf;
+    }
+  }
+  //  last chunk     chunktype literal   dist       lengths    lmask   dmask
+  let final = false, type = 0, hLit = 0, hDist = 0, hcLen = 0, ml = 0, md = 0;
+  //  bitpos   bytes
+  let pos = 0, bt = 0;
+  //  len                dist
+  let lm: Uint16Array, dm: Uint16Array;
+  while (!final) {
+    // BFINAL - this is only 1 when last chunk is next
+    final = bits(dat, pos, 1) as unknown as boolean;
+    // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
+    type = bits(dat, pos + 1, 2);
+    pos += 3;
+    if (!type) {
+      // go to end of byte boundary
+      if (pos & 7) pos += 8 - (pos & 7);
+      const s = (pos >>> 3) + 4, l = dat[s - 4] | (dat[s - 3] << 8);
+      // ensure size
+      if (noBuf) cbuf(bt + l);
+      // Copy over uncompressed data
+      buf.set(dat.subarray(s, s + l), bt);
+      // Get new bitpos, update byte count
+      pos = (s + l) << 3, bt += l;
+      continue;
+    }
+    // Make sure the buffer can hold this + the largest possible addition
+    if (noBuf) cbuf(bt + MC);
+    if (type == 1) {
+      lm = flm;
+      dm = fdm;
+      ml = fml;
+      md = fmd;
+    }
+    else if (type == 2) {
+      hLit = bits(dat, pos, 5) + 257;
+      hDist = bits(dat, pos + 5, 5) + 1;
+      hcLen = bits(dat, pos + 10, 4) + 4;
+      pos += 14;
+      // length+distance tree
+      const ldt = new u8(hLit + hDist);
+      // code length tree
+      const clt = new u8(19);
+      for (let i = 0; i < hcLen; ++i) {
+        // use index map to get real code
+        clt[clim[i]] = bits(dat, pos + i * 3, 3);
+      }
+      pos += hcLen * 3;
+      // code lengths bits
+      const clb = max(clt);
+      // code lengths map
+      const clm = hTree(clt, clb);
+      for (let i = 0; i < ldt.length;) {
+        const r = clm[bits(dat, pos, clb)];
+        // bits read
+        pos += r & 15;
+        // symbol
+        const s = r >>> 4;
+        // code length to copy
+        if (s < 16) {
+          ldt[i++] = s;
+        } else {
+          //  copy   count
+          let c = 0, n = 0;
+          if (s == 16) n = 3 + bits(dat, pos, 2), pos += 2, c = ldt[i - 1];
+          else if (s == 17) n = 3 + bits(dat, pos, 3), pos += 3;
+          else if (s == 18) n = 11 + bits(dat, pos, 7), pos += 7;
+          while (n--) ldt[i++] = c;
+        }
+      }
+      //    length tree                 distance tree
+      const lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
+      // max length bits
+      const mlb = max(lt)
+      // max dist bits
+      const mdb = max(dt);
+      ml = (1 << mlb) - 1;
+      lm = hTree(lt, mlb);
+      md = (1 << mdb) - 1;
+      dm = hTree(dt, mdb);
+    }
+    while (1) {
+      // bits read, code
+      const c = lm[bits16(dat, pos) & ml];
+      pos += c & 15;
+      // code
+      const sym = c >>> 4;
+      if (sym < 256) buf[bt++] = sym;
+      else if (sym == 256) break;
+      else {
+        let end = bt + sym - 254;
+        // no extra bits needed if less
+        if (sym > 264) {
+          // index
+          const i = sym - 257;
+          end = bt + bits(dat, pos, fleb[i]) + fl[i];
+          pos += fleb[i];
+        }
+        // dist
+        const d = dm[bits16(dat, pos) & md];
+        pos += d & 15;
+        const dsym = d >>> 4;
+        let dt = fd[dsym];
+        if (dsym > 3) {
+          dt += bits16(dat, pos) & ((1 << fdeb[dsym]) - 1);
+          pos += fdeb[dsym];
+        }
+        if (noBuf) cbuf(bt + MC);
+        while (bt < end) {
+          buf[bt] = buf[bt++ - dt];
+          buf[bt] = buf[bt++ - dt];
+          buf[bt] = buf[bt++ - dt];
+          buf[bt] = buf[bt++ - dt];
+        }
+        bt = end;
+      }
+    }
+  }
+  return buf.slice(0, bt);
+}
+
+
+export { inflate };

+ 8 - 0
tsconfig.esm.json

@@ -0,0 +1,8 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "declaration": false,
+    "target": "ESNext",
+    "outDir": "esm"
+  }
+}

+ 7 - 0
tsconfig.json

@@ -0,0 +1,7 @@
+{
+  "compilerOptions": {
+    "declaration": true,
+    "outDir": "lib/"
+  },
+  "files": ["index.ts"]
+}

+ 8 - 0
yarn.lock

@@ -0,0 +1,8 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+typescript@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
+  integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==