From 70fccdde1853775cafaa27cd54655ec6f47a6962 Mon Sep 17 00:00:00 2001 From: Ze-Zheng Wu Date: Tue, 22 Nov 2022 00:34:28 +0800 Subject: [PATCH] fix: encode and encoder hooks terms, comments, APIs, etc. --- src/encode.ts | 162 +++++++++++++++++++++++++++----------------------- 1 file changed, 87 insertions(+), 75 deletions(-) diff --git a/src/encode.ts b/src/encode.ts index db4823f..ed8b345 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -3,46 +3,54 @@ import { BData, BUFF_L, BUFF_E, BUFF_D } from "./utils/codec.js"; import { iterableSort } from "./utils/misc.js"; /** - * encode hook handler + * encoder hook */ -export type EncoderHookHandler = ( +export type EncoderHook = ( result: IteratorResult ) => void; /** - * encoder hooks + * encoder hook path */ -export type EncoderHooks = TrieMap< - Iterable, - EncoderHookHandler ->; +export type EncoderHookPath = Iterable; -const consumedHooks = new WeakMap(); +/** + * encoder hook system + */ +export type EncoderHookSystem = TrieMap; + +/** + * a global weakmap that keeps all consumed hook systems, for internal use + */ +const consumedHookSystems = new WeakMap(); /** - * bencode readablestream underlying source + * bencode readable stream underlying source */ class EncoderUnderlyingSource implements UnderlyingSource { textEncoder = new TextEncoder(); textDecoder = new TextDecoder(); data: BData; - path: (string | number)[] = []; - hooks?: EncoderHooks; - consumedHookHandler = new WeakMap(); - constructor(data: BData, hooks?: EncoderHooks) { + path: (EncoderHookPath extends Iterable + ? PathElement + : never)[] = []; + hookSystem?: EncoderHookSystem; + attachedHooks = new WeakMap(); + constructor(data: BData, hookSystem?: EncoderHookSystem) { this.data = data; - this.hooks = hooks; - if (hooks) { - consumedHooks.set(hooks, true); + this.hookSystem = hookSystem; + if (hookSystem) { + consumedHookSystems.set(hookSystem, true); } } start(controller: ReadableStreamController) { this.encode(this.data, controller); - if (this.hooks) { - for (const hookHandler of this.hooks.values()) { + // wind up + if (this.hookSystem) { + for (const hook of this.hookSystem.values()) { // only done once, in case of closing controller twice - if (!this.consumedHookHandler.get(hookHandler)) { - hookHandler({ value: undefined, done: true }); + if (!this.attachedHooks.get(hook)) { + hook({ value: undefined, done: true }); } } } @@ -94,13 +102,13 @@ class EncoderUnderlyingSource implements UnderlyingSource { for (const member of data) { // push path this.path.push(counter); - // if hooks are registered - if (this.hooks) { - const hookHandler = this.hooks.get(this.path); - if (hookHandler) { - const newController = addHandler(controller, hookHandler); + // if hook system is provided + if (this.hookSystem) { + const hook = this.hookSystem.get(this.path); + if (hook) { + const newController = attachHook(controller, hook); this.encode(member, newController); - hookHandler({ value: undefined, done: true }); + hook({ value: undefined, done: true }); } else { this.encode(member, controller); } @@ -135,14 +143,14 @@ class EncoderUnderlyingSource implements UnderlyingSource { this.path.push(key); // encode key this.encode(key, controller); - // if hooks are registered - if (this.hooks) { - const hookHandler = this.hooks.get(this.path); - if (hookHandler) { - const newController = addHandler(controller, hookHandler); + // if hook system is provided + if (this.hookSystem) { + const hook = this.hookSystem.get(this.path); + if (hook) { + const newController = attachHook(controller, hook); this.encode(value, newController); - hookHandler({ value: undefined, done: true }); - this.consumedHookHandler.set(hookHandler, true); + hook({ value: undefined, done: true }); + this.attachedHooks.set(hook, true); } else { this.encode(value, controller); } @@ -166,14 +174,14 @@ class EncoderUnderlyingSource implements UnderlyingSource { this.path.push(key); // encode key this.encode(key, controller); - // if hooks are registered - if (this.hooks) { - const hookHandler = this.hooks.get(this.path); - if (hookHandler) { - const newController = addHandler(controller, hookHandler); + // if hook system is provided + if (this.hookSystem) { + const hook = this.hookSystem.get(this.path); + if (hook) { + const newController = attachHook(controller, hook); this.encode(value, newController); - hookHandler({ value: undefined, done: true }); - this.consumedHookHandler.set(hookHandler, true); + hook({ value: undefined, done: true }); + this.attachedHooks.set(hook, true); } else { this.encode(value, controller); } @@ -187,24 +195,24 @@ class EncoderUnderlyingSource implements UnderlyingSource { } /** - * BEncode + * Bencode * @param data - * @param hooks + * @param hookSystem * @returns readable stream of the bencoded data */ -export function encode(data: BData, hooks?: EncoderHooks) { - return new ReadableStream(new EncoderUnderlyingSource(data, hooks)); +export function encode(data: BData, hookSystem?: EncoderHookSystem) { + return new ReadableStream(new EncoderUnderlyingSource(data, hookSystem)); } /** - * Add handler to controller + * Attach hook to controller * @param controller - * @param hookHandler + * @param hook * @returns */ -function addHandler( +function attachHook( controller: ReadableStreamController, - hookHandler: EncoderHookHandler + hook: EncoderHook ) { const newController = new Proxy(controller, { get: function (target, prop, receiver) { @@ -212,7 +220,7 @@ function addHandler( case "enqueue": return ((chunk: Uint8Array) => { target.enqueue(chunk); - hookHandler({ + hook({ value: chunk, done: false, }); @@ -226,21 +234,20 @@ function addHandler( } /** - * Get a uint8 array stream hook handler - * @returns a uint8 array readable stream + * Register a hook and consume the result as an uint8 array readable stream + * @param path + * @param hookSystem + * @returns an uint8 array readable stream */ export function useUint8ArrayStreamHook( - path: Iterable, - hooks: EncoderHooks -): [ReadableStream] { + path: EncoderHookPath, + hookSystem: EncoderHookSystem +): ReadableStream { const ref = { controller: null as ReadableStreamController | null, }; - const hookHandler = ({ - value, - done, - }: IteratorResult) => { + const hook = ({ value, done }: IteratorResult) => { if (ref.controller === null) { return; } @@ -256,7 +263,7 @@ export function useUint8ArrayStreamHook( ref.controller = controller; }, pull(controller) { - if (!consumedHooks.get(hooks)) { + if (!consumedHookSystems.get(hookSystem)) { // prevent endless awaiting when this readable stream is consumed as promise console.warn("You need to call encode() first and then consume hooks."); controller.close(); @@ -265,33 +272,38 @@ export function useUint8ArrayStreamHook( }, }); - hooks.set(path, hookHandler); + // register hook in hook system + hookSystem.set(path, hook); - return [readableStream]; + return readableStream; } /** - * Get an array buffer promise hook handler - * @returns an array buffer + * Register a hook and consume the result as an array buffer promise + * @param path + * @param hookSystem + * @returns an array buffer promise */ export function useArrayBufferPromiseHook( - path: Iterable, - hooks: EncoderHooks -): [Promise] { - const [readableStream] = useUint8ArrayStreamHook(path, hooks); + path: EncoderHookPath, + hookSystem: EncoderHookSystem +): Promise { + const readableStream = useUint8ArrayStreamHook(path, hookSystem); const arrayBufferPromise = new Response(readableStream).arrayBuffer(); - return [arrayBufferPromise]; + return arrayBufferPromise; } /** - * Get an text promise hook handler - * @returns an text promise + * Register a hook and consume the result as a text promise + * @param path + * @param hookSystem + * @returns a text promise */ export function useTextPromiseHook( - path: Iterable, - hooks: EncoderHooks -): [Promise] { - const [readableStream] = useUint8ArrayStreamHook(path, hooks); + path: EncoderHookPath, + hookSystem: EncoderHookSystem +): Promise { + const readableStream = useUint8ArrayStreamHook(path, hookSystem); const textPromise = new Response(readableStream).text(); - return [textPromise]; + return textPromise; }