Arjun Barrett il y a 4 ans
Parent
commit
71f6fd034d
8 fichiers modifiés avec 78 ajouts et 20 suppressions
  1. 10 1
      README.md
  2. 3 1
      demo/App.tsx
  3. 1 1
      demo/components/file-picker/index.tsx
  4. 0 1
      demo/util/workers.ts
  5. 2 1
      docs/README.md
  6. 26 0
      docs/interfaces/asyncunzipoptions.md
  7. 1 1
      package.json
  8. 35 14
      src/index.ts

+ 10 - 1
README.md

@@ -222,7 +222,7 @@ As you may have guessed, there is an asynchronous version of every method as wel
 
 Note that there is a significant initial overhead to using workers of about 70ms, so it's best to avoid the asynchronous API unless necessary. However, if you're compressing multiple large files at once, or the synchronous API causes the main thread to hang for too long, the callback APIs are an order of magnitude better.
 ```js
-import { gzip, zlib, AsyncGzip, zip } from 'fflate';
+import { gzip, zlib, AsyncGzip, zip, strFromU8 } from 'fflate';
 
 // Workers will work in almost any browser (even IE11!)
 // However, they fail below Node v12 without the --experimental-worker
@@ -291,6 +291,15 @@ gzs.terminate();
 // significant for multiple large files; less so for many small ones.
 zip({ f1: aMassiveFile, 'f2.txt': anotherMassiveFile }, (err, data) => {
   // Save the ZIP file
+});
+
+// unzip is the only async function without support for consume option
+// Also parallelized, so unzip is also often much faster than unzipSync
+unzip(aMassiveZIPFile, (err, unzipped) => {
+  // If the archive has data.xml, log it here
+  console.log(unzipped['data.xml']);
+  // Conversion to string
+  console.log(strFromU8(unzipped['data.xml']))
 })
 ```
 

+ 3 - 1
demo/App.tsx

@@ -3,6 +3,7 @@ import FilePicker from './components/file-picker';
 import CodeBox from './components/code-box';
 
 const App: FC = () => {
+  const [err, setErr] = useState<string | Error | null>(null);
   const [files, setFiles] = useState<File[] | null>([]);
   const cbRef = useRef<HTMLDivElement>(null);
   useEffect(() => {
@@ -69,7 +70,8 @@ const App: FC = () => {
           flexDirection: 'column',
           marginBottom: '2vh'
         }}>
-          <FilePicker allowDirs onFiles={setFiles} onError={console.log} onDrag={() => {}}>
+          <FilePicker allowDirs onFiles={setFiles} onError={setErr} onDrag={() => {}}>
+            {err && <div style={{ color: 'red' }}>Error: {err}</div>}
             <div>{files ? ((files.length || 'No') + ' file' + (files.length == 1 ? '' : 's') + ' selected') : 'Loading...'}</div>
             <br />
           </FilePicker>

+ 1 - 1
demo/components/file-picker/index.tsx

@@ -41,7 +41,7 @@ const FilePicker: FC<{
   onDrag(on: boolean): void;
   onError(err: string | Error): void;
   allowDirs: boolean;
-} & HTMLAttributes<HTMLDivElement>
+} & Omit<HTMLAttributes<HTMLDivElement>, 'onError'>
 > = ({ onFiles, onDrag, onError, style, allowDirs, children, ...props }) => {
   const inputRef = useRef<HTMLInputElement>(null);
   const dirInputRef = useRef<HTMLInputElement>(null);

+ 0 - 1
demo/util/workers.ts

@@ -106,7 +106,6 @@ onmessage = (ev: MessageEvent<[string, string]>) => {
         if (ev.data) {
           zip[ev.data[0]] = ev.data[1];
         } else {
-          console.log(zip);
           const buf = UZIP.encode(zip);
           wk.postMessage([new Uint8Array(buf), true], [buf]);
         }

+ 2 - 1
docs/README.md

@@ -26,6 +26,7 @@
 * [AsyncGzipOptions](interfaces/asyncgzipoptions.md)
 * [AsyncInflateOptions](interfaces/asyncinflateoptions.md)
 * [AsyncTerminable](interfaces/asyncterminable.md)
+* [AsyncUnzipOptions](interfaces/asyncunzipoptions.md)
 * [AsyncUnzlibOptions](interfaces/asyncunzliboptions.md)
 * [AsyncZipOptions](interfaces/asynczipoptions.md)
 * [AsyncZippable](interfaces/asynczippable.md)
@@ -89,7 +90,7 @@ ___
 
 Ƭ  **AsyncZippableFile**: Uint8Array \| []
 
-A file that can be used to asynchronously createa a ZIP archive
+A file that can be used to asynchronously create a ZIP archive
 
 ___
 

+ 26 - 0
docs/interfaces/asyncunzipoptions.md

@@ -0,0 +1,26 @@
+# Interface: AsyncUnzipOptions
+
+Options for asynchronously expanding a ZIP archive
+
+## Hierarchy
+
+* AsyncOptions
+
+  ↳ **AsyncUnzipOptions**
+
+## Index
+
+### Properties
+
+* [consume](asyncunzipoptions.md#consume)
+
+## Properties
+
+### consume
+
+• `Optional` **consume**: boolean
+
+*Inherited from [AsyncDeflateOptions](asyncdeflateoptions.md).[consume](asyncdeflateoptions.md#consume)*
+
+Whether or not to "consume" the source data. This will make the typed array/buffer you pass in
+unusable but will increase performance and reduce memory usage.

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "fflate",
-  "version": "0.3.10",
+  "version": "0.3.11",
   "description": "High performance (de)compression in an 8kB package",
   "main": "./lib/index.js",
   "module": "./esm/index.mjs",

+ 35 - 14
src/index.ts

@@ -320,7 +320,7 @@ const inflt = (dat: Uint8Array, buf?: Uint8Array, st?: InflateState) => {
   return bt == buf.length ? buf : slc(buf, 0, bt);
 }
 
-// starting at p, write the minimum number of bits that can hold v to ds
+// starting at p, write the minimum number of bits that can hold v to d
 const wbits = (d: Uint8Array, p: number, v: number) => {
   v <<= p & 7;
   const o = p >>> 3;
@@ -328,7 +328,7 @@ const wbits = (d: Uint8Array, p: number, v: number) => {
   d[o + 1] |= v >>> 8;
 }
 
-// starting at p, write the minimum number of bits (>8) that can hold v to ds
+// starting at p, write the minimum number of bits (>8) that can hold v to d
 const wbits16 = (d: Uint8Array, p: number, v: number) => {
   v <<= p & 7;
   const o = p >>> 3;
@@ -1268,7 +1268,7 @@ export function inflate(data: Uint8Array, opts: AsyncInflateOptions, cb: FlateCa
 export function inflate(data: Uint8Array, cb: FlateCallback): AsyncTerminable;
 export function inflate(data: Uint8Array, opts: AsyncInflateOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
-  if (!cb) throw 'no callback';
+  if (typeof cb != 'function') throw 'no callback';
   return cbify(data, opts as AsyncInflateOptions, [
     bInflt
   ], ev => pbf(inflateSync(ev.data[0], gu8(ev.data[1]))), 1, cb);
@@ -1396,7 +1396,7 @@ export function gzip(data: Uint8Array, opts: AsyncGzipOptions, cb: FlateCallback
 export function gzip(data: Uint8Array, cb: FlateCallback): AsyncTerminable;
 export function gzip(data: Uint8Array, opts: AsyncGzipOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
-  if (!cb) throw 'no callback';
+  if (typeof cb != 'function') throw 'no callback';
   return cbify(data, opts as AsyncGzipOptions, [
     bDflt,
     gze,
@@ -1513,7 +1513,7 @@ export function gunzip(data: Uint8Array, opts: AsyncGunzipOptions, cb: FlateCall
 export function gunzip(data: Uint8Array, cb: FlateCallback): AsyncTerminable;
 export function gunzip(data: Uint8Array, opts: AsyncGunzipOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
-  if (!cb) throw 'no callback';
+  if (typeof cb != 'function') throw 'no callback';
   return cbify(data, opts as AsyncGunzipOptions, [
     bInflt,
     guze,
@@ -1638,7 +1638,7 @@ export function zlib(data: Uint8Array, opts: AsyncZlibOptions, cb: FlateCallback
 export function zlib(data: Uint8Array, cb: FlateCallback): AsyncTerminable;
 export function zlib(data: Uint8Array, opts: AsyncZlibOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
-  if (!cb) throw 'no callback';
+  if (typeof cb != 'function') throw 'no callback';
   return cbify(data, opts as AsyncZlibOptions, [
     bDflt,
     zle,
@@ -1753,7 +1753,7 @@ export function unzlib(data: Uint8Array, opts: AsyncGunzipOptions, cb: FlateCall
 export function unzlib(data: Uint8Array, cb: FlateCallback): AsyncTerminable;
 export function unzlib(data: Uint8Array, opts: AsyncGunzipOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
-  if (!cb) throw 'no callback';
+  if (typeof cb != 'function') throw 'no callback';
   return cbify(data, opts as AsyncUnzlibOptions, [
     bInflt,
     zule,
@@ -1867,7 +1867,7 @@ export function decompress(data: Uint8Array, opts: AsyncInflateOptions, cb: Flat
 export function decompress(data: Uint8Array, cb: FlateCallback): AsyncTerminable;
 export function decompress(data: Uint8Array, opts: AsyncInflateOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
-  if (!cb) throw 'no callback';
+  if (typeof cb != 'function') throw 'no callback';
   return (data[0] == 31 && data[1] == 139 && data[2] == 8)
     ? gunzip(data, opts as AsyncInflateOptions, cb)
     : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))
@@ -1899,13 +1899,18 @@ export interface ZipOptions extends DeflateOptions, Pick<GzipOptions, 'mtime'> {
  */
 export interface AsyncZipOptions extends AsyncDeflateOptions, Pick<AsyncGzipOptions, 'mtime'> {}
 
+/**
+ * Options for asynchronously expanding a ZIP archive
+ */
+export interface AsyncUnzipOptions extends AsyncOptions {}
+
 /**
  * A file that can be used to create a ZIP archive
  */
 export type ZippableFile = Uint8Array | [Uint8Array, ZipOptions];
 
 /**
- * A file that can be used to asynchronously createa a ZIP archive
+ * A file that can be used to asynchronously create a ZIP archive
  */
 export type AsyncZippableFile = Uint8Array | [Uint8Array, AsyncZipOptions];
 
@@ -2077,6 +2082,7 @@ export function zip(data: AsyncZippable, opts: AsyncZipOptions, cb: FlateCallbac
 export function zip(data: AsyncZippable, cb: FlateCallback): AsyncTerminable;
 export function zip(data: AsyncZippable, opts: AsyncZipOptions | FlateCallback, cb?: FlateCallback) {
   if (!cb) cb = opts as FlateCallback, opts = {};
+  if (typeof cb != 'function') throw 'no callback';
   const r: FlatZippable<true> = {};
   fltn(data, '', r, opts as AsyncZipOptions);
   const k = Object.keys(r);
@@ -2106,7 +2112,6 @@ export function zip(data: AsyncZippable, opts: AsyncZipOptions | FlateCallback,
     c.p(file);
     const n = strToU8(fn), s = n.length;
     const t = p.level == 0 ? 0 : 8;
-    if (n.length > 65535) throw 'filename too long';
     const cbl: FlateCallback = (e, d) => {
       if (e) {
         tAll();
@@ -2127,9 +2132,15 @@ export function zip(data: AsyncZippable, opts: AsyncZipOptions | FlateCallback,
         if (!--lft) cbf();
       }
     }
+    if (n.length > 65535) cbl(new Error('filename too long'), null);
     if (!t) cbl(null, file);
-    else if (m < 160000) cbl(null, deflateSync(file, opts as AsyncZipOptions));
-    else term.push(deflate(file, opts as AsyncZipOptions, cbl));
+    else if (m < 160000) {
+      try {
+        cbl(null, deflateSync(file, opts as AsyncZipOptions));
+      } catch(e) {
+        cbl(e, null);
+      }
+    } else term.push(deflate(file, opts as AsyncZipOptions, cbl));
   }
   return tAll;
 }
@@ -2185,6 +2196,7 @@ export function zipSync(data: Zippable, opts: ZipOptions = {}) {
  * @returns A function that can be used to immediately terminate the unzipping
  */
 export function unzip(data: Uint8Array, cb: UnzipCallback): AsyncTerminable {
+  if (typeof cb != 'function') throw 'no callback';
   const term: AsyncTerminable[] = [];
   const tAll = () => {
     for (let i = 0; i < term.length; ++i) term[i]();
@@ -2192,7 +2204,10 @@ export function unzip(data: Uint8Array, cb: UnzipCallback): AsyncTerminable {
   const files: Unzipped = {};
   let e = data.length - 22;
   for (; b4(data, e) != 0x6054B50; --e) {
-    if (!e || data.length - e > 65558) throw 'invalid zip file';
+    if (!e || data.length - e > 65558) {
+      cb(new Error('invalid zip file'), null);
+      return;
+    }
   };
   let lft = b2(data, e + 8);
   if (!lft) cb(null, {});
@@ -2214,7 +2229,13 @@ export function unzip(data: Uint8Array, cb: UnzipCallback): AsyncTerminable {
     if (!c) cbl(null, slc(data, b, b + sc))
     else if (c == 8) {
       const infl = data.subarray(b, sc ? b + sc : data.length);
-      if (sc < 320000) cbl(null, inflateSync(infl, su != null && new u8(su)));
+      if (sc < 320000) {
+        try {
+          cbl(null, inflateSync(infl, su != null && new u8(su)));
+        } catch(e) {
+          cbl(e, null);
+        }
+      }
       else inflate(infl, { size: su }, cbl);
     } else throw 'unknown compression type ' + c;
   }