feat: support skip collect child for memory and setup stream start offset
This commit is contained in:
		
							parent
							
								
									bbc9c86531
								
							
						
					
					
						commit
						85eecbf6ac
					
				@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "konoebml",
 | 
			
		||||
  "version": "0.1.0-rc.2",
 | 
			
		||||
  "version": "0.1.0-rc.3",
 | 
			
		||||
  "description": "A modern JavaScript implementation of EBML RFC8794",
 | 
			
		||||
  "main": "./dist/index.cjs",
 | 
			
		||||
  "module": "./dist/index.js",
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { type EbmlTagIdType, isEbmlMasterTagId } from './models/enums';
 | 
			
		||||
import type { EbmlTagTrait } from './models/tag-trait';
 | 
			
		||||
import type { DecodeContentOptions, EbmlTagTrait } from './models/tag-trait';
 | 
			
		||||
import type { FileDataViewController } from './adapters';
 | 
			
		||||
import {
 | 
			
		||||
  checkVintSafeSize,
 | 
			
		||||
@ -71,8 +71,9 @@ export async function decodeEbmlTagHeader(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function* decodeEbmlContent(
 | 
			
		||||
  controller: FileDataViewController
 | 
			
		||||
  options: DecodeContentOptions
 | 
			
		||||
): AsyncGenerator<EbmlTagTrait, void, unknown> {
 | 
			
		||||
  const controller = options.dataViewController;
 | 
			
		||||
  while (true) {
 | 
			
		||||
    const offset = controller.getOffset();
 | 
			
		||||
 | 
			
		||||
@ -112,7 +113,7 @@ export async function* decodeEbmlContent(
 | 
			
		||||
      parent: undefined,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    for await (const item of tag.decodeContent(controller)) {
 | 
			
		||||
    for await (const item of tag.decodeContent(options)) {
 | 
			
		||||
      yield item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,9 @@
 | 
			
		||||
import { Queue } from 'mnemonist';
 | 
			
		||||
import type { FileDataViewController } from './adapters';
 | 
			
		||||
import type { EbmlTagTrait } from './models/tag-trait';
 | 
			
		||||
import type {
 | 
			
		||||
  DecodeContentCollectChildPredicate,
 | 
			
		||||
  EbmlTagTrait,
 | 
			
		||||
} from './models/tag-trait';
 | 
			
		||||
import { decodeEbmlContent } from './decode-utils';
 | 
			
		||||
import { StreamFlushReason, UnreachableOrLogicError } from './errors';
 | 
			
		||||
import { dataViewSlice } from './tools';
 | 
			
		||||
@ -10,6 +13,11 @@ export type EbmlStreamDecoderChunkType =
 | 
			
		||||
  | ArrayBuffer
 | 
			
		||||
  | ArrayBufferLike;
 | 
			
		||||
 | 
			
		||||
export interface EbmlDecodeStreamTransformerOptions {
 | 
			
		||||
  collectChild?: DecodeContentCollectChildPredicate;
 | 
			
		||||
  streamStartOffset?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class EbmlDecodeStreamTransformer
 | 
			
		||||
  implements
 | 
			
		||||
    Transformer<EbmlStreamDecoderChunkType, EbmlTagTrait>,
 | 
			
		||||
@ -23,6 +31,11 @@ export class EbmlDecodeStreamTransformer
 | 
			
		||||
  private _tickIdleCallback: VoidFunction | undefined;
 | 
			
		||||
  private _currentTask: Promise<void> | undefined;
 | 
			
		||||
  private _writeBuffer = new Queue<EbmlTagTrait>();
 | 
			
		||||
  public readonly options: EbmlDecodeStreamTransformerOptions;
 | 
			
		||||
 | 
			
		||||
  constructor(options: EbmlDecodeStreamTransformerOptions = {}) {
 | 
			
		||||
    this.options = options;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getBuffer(): Uint8Array {
 | 
			
		||||
    return this._buffer;
 | 
			
		||||
@ -171,7 +184,10 @@ export class EbmlDecodeStreamTransformer
 | 
			
		||||
    if (!this._currentTask && !isFlush) {
 | 
			
		||||
      const decode = async () => {
 | 
			
		||||
        try {
 | 
			
		||||
          for await (const tag of decodeEbmlContent(this)) {
 | 
			
		||||
          for await (const tag of decodeEbmlContent({
 | 
			
		||||
            collectChild: this.options.collectChild,
 | 
			
		||||
            dataViewController: this,
 | 
			
		||||
          })) {
 | 
			
		||||
            this.tryEnqueueToBuffer(tag);
 | 
			
		||||
          }
 | 
			
		||||
          this._currentTask = undefined;
 | 
			
		||||
@ -189,7 +205,7 @@ export class EbmlDecodeStreamTransformer
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async start(ctrl: TransformStreamDefaultController<EbmlTagTrait>) {
 | 
			
		||||
    this._offset = 0;
 | 
			
		||||
    this._offset = this.options.streamStartOffset ?? 0;
 | 
			
		||||
    this._buffer = new Uint8Array(0);
 | 
			
		||||
    this._requests.clear();
 | 
			
		||||
    this._tickIdleCallback = undefined;
 | 
			
		||||
@ -223,14 +239,17 @@ export class EbmlDecodeStreamTransformer
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface EbmlStreamDecoderOptions
 | 
			
		||||
  extends EbmlDecodeStreamTransformerOptions {}
 | 
			
		||||
 | 
			
		||||
export class EbmlStreamDecoder extends TransformStream<
 | 
			
		||||
  EbmlStreamDecoderChunkType,
 | 
			
		||||
  EbmlTagTrait
 | 
			
		||||
> {
 | 
			
		||||
  public readonly transformer: EbmlDecodeStreamTransformer;
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const transformer = new EbmlDecodeStreamTransformer();
 | 
			
		||||
  constructor(options: EbmlStreamDecoderOptions = {}) {
 | 
			
		||||
    const transformer = new EbmlDecodeStreamTransformer(options);
 | 
			
		||||
    super(transformer);
 | 
			
		||||
    this.transformer = transformer;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/index.ts
									
									
									
									
									
								
							@ -1,8 +1,22 @@
 | 
			
		||||
export { EbmlBlockTag } from './models/tag-block';
 | 
			
		||||
export { EbmlDataTag } from './models/tag-data';
 | 
			
		||||
export { EbmlMasterTag } from './models/tag-master';
 | 
			
		||||
export { EbmlSimpleBlockTag } from './models/tag-simple-block';
 | 
			
		||||
export { EbmlTagTrait } from './models/tag-trait';
 | 
			
		||||
export {
 | 
			
		||||
  EbmlBlockTag,
 | 
			
		||||
  type CreateEbmlBlockTagOptions,
 | 
			
		||||
} from './models/tag-block';
 | 
			
		||||
export { EbmlDataTag, type CreateEbmlDataTagOptions } from './models/tag-data';
 | 
			
		||||
export {
 | 
			
		||||
  EbmlMasterTag,
 | 
			
		||||
  type CreateEbmlMasterTagOptions,
 | 
			
		||||
} from './models/tag-master';
 | 
			
		||||
export {
 | 
			
		||||
  EbmlSimpleBlockTag,
 | 
			
		||||
  type CreateEbmlSimpleBlockTagOptions,
 | 
			
		||||
} from './models/tag-simple-block';
 | 
			
		||||
export {
 | 
			
		||||
  EbmlTagTrait,
 | 
			
		||||
  type DecodeContentCollectChildPredicate,
 | 
			
		||||
  type DecodeContentOptions,
 | 
			
		||||
  type CreateEbmlTagOptions,
 | 
			
		||||
} from './models/tag-trait';
 | 
			
		||||
export {
 | 
			
		||||
  createEbmlTag,
 | 
			
		||||
  createEbmlTagForManuallyBuild,
 | 
			
		||||
@ -15,6 +29,8 @@ export {
 | 
			
		||||
  EbmlStreamDecoder,
 | 
			
		||||
  EbmlDecodeStreamTransformer,
 | 
			
		||||
  type EbmlStreamDecoderChunkType,
 | 
			
		||||
  type EbmlStreamDecoderOptions,
 | 
			
		||||
  type EbmlDecodeStreamTransformerOptions,
 | 
			
		||||
} from './decoder';
 | 
			
		||||
export {
 | 
			
		||||
  EbmlStreamEncoder,
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ import {
 | 
			
		||||
  EbmlTagIdEnum,
 | 
			
		||||
} from './enums';
 | 
			
		||||
import { EbmlElementType } from './enums';
 | 
			
		||||
import type { FileDataViewController } from '../adapters';
 | 
			
		||||
import type { DecodeContentOptions } from './tag-trait';
 | 
			
		||||
 | 
			
		||||
export interface CreateEbmlBlockTagOptions
 | 
			
		||||
  extends Omit<CreateEbmlDataTagOptions, 'id' | 'type'> {
 | 
			
		||||
@ -77,7 +77,8 @@ export class EbmlBlockTag extends EbmlDataTag {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/useYield: <explanation>
 | 
			
		||||
  async *decodeContentImpl(controller: FileDataViewController) {
 | 
			
		||||
  async *decodeContentImpl(options: DecodeContentOptions) {
 | 
			
		||||
    const controller = options.dataViewController;
 | 
			
		||||
    const offset = controller.getOffset();
 | 
			
		||||
    const view = await controller.read(offset, this.contentLength, true);
 | 
			
		||||
    const track = readVint(view)!;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,10 @@
 | 
			
		||||
import { type CreateEbmlTagOptions, EbmlTagTrait } from './tag-trait';
 | 
			
		||||
import {
 | 
			
		||||
  type CreateEbmlTagOptions,
 | 
			
		||||
  type DecodeContentOptions,
 | 
			
		||||
  EbmlTagTrait,
 | 
			
		||||
} from './tag-trait';
 | 
			
		||||
import { EbmlElementType } from './enums';
 | 
			
		||||
import {
 | 
			
		||||
  dataViewSlice,
 | 
			
		||||
  dataViewSliceToBuf,
 | 
			
		||||
  readAscii,
 | 
			
		||||
  readFloat,
 | 
			
		||||
@ -15,7 +18,6 @@ import {
 | 
			
		||||
  writeUtf8,
 | 
			
		||||
} from '../tools';
 | 
			
		||||
import { EbmlTagPosition } from './enums';
 | 
			
		||||
import type { FileDataViewController } from 'src/adapters';
 | 
			
		||||
 | 
			
		||||
export type CreateEbmlDataTagOptions = Omit<CreateEbmlTagOptions, 'position'>;
 | 
			
		||||
 | 
			
		||||
@ -30,7 +32,8 @@ export class EbmlDataTag extends EbmlTagTrait {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/useYield: <explanation>
 | 
			
		||||
  override async *decodeContentImpl(controller: FileDataViewController) {
 | 
			
		||||
  override async *decodeContentImpl(options: DecodeContentOptions) {
 | 
			
		||||
    const controller = options.dataViewController;
 | 
			
		||||
    const offset = controller.getOffset();
 | 
			
		||||
    const view = await controller.read(offset, this.contentLength, true);
 | 
			
		||||
    switch (this.type) {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,12 @@
 | 
			
		||||
import { type CreateEbmlTagOptions, EbmlTagTrait } from './tag-trait';
 | 
			
		||||
import {
 | 
			
		||||
  type CreateEbmlTagOptions,
 | 
			
		||||
  type DecodeContentOptions,
 | 
			
		||||
  EbmlTagTrait,
 | 
			
		||||
} from './tag-trait';
 | 
			
		||||
import { EbmlElementType, EbmlTagPosition, isEbmlMasterTagId } from './enums';
 | 
			
		||||
import { decodeEbmlTagHeader } from '../decode-utils';
 | 
			
		||||
import { createEbmlTag } from 'src/factory';
 | 
			
		||||
import type { EbmlMasterTagIdType } from './enums';
 | 
			
		||||
import type { FileDataViewController } from '../adapters';
 | 
			
		||||
 | 
			
		||||
export interface CreateEbmlMasterTagOptions
 | 
			
		||||
  extends Omit<CreateEbmlTagOptions, 'position' | 'type' | 'id'> {
 | 
			
		||||
@ -35,7 +38,9 @@ export class EbmlMasterTag extends EbmlTagTrait {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async *decodeContentImpl(controller: FileDataViewController) {
 | 
			
		||||
  async *decodeContentImpl(options: DecodeContentOptions) {
 | 
			
		||||
    const controller = options.dataViewController;
 | 
			
		||||
    const collectChild = options.collectChild;
 | 
			
		||||
    while (true) {
 | 
			
		||||
      const offset = controller.getOffset();
 | 
			
		||||
 | 
			
		||||
@ -79,13 +84,22 @@ export class EbmlMasterTag extends EbmlTagTrait {
 | 
			
		||||
        parent: this,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      for await (const item of tag.decodeContent(controller)) {
 | 
			
		||||
      for await (const item of tag.decodeContent(options)) {
 | 
			
		||||
        yield item;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      tag.endOffset = controller.getOffset();
 | 
			
		||||
 | 
			
		||||
      this._children.push(tag);
 | 
			
		||||
      let shouldCollectChild: boolean;
 | 
			
		||||
      if (typeof collectChild === 'function') {
 | 
			
		||||
        shouldCollectChild = !!collectChild(tag, this);
 | 
			
		||||
      } else {
 | 
			
		||||
        shouldCollectChild = !!collectChild;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (shouldCollectChild) {
 | 
			
		||||
        this._children.push(tag);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      yield tag;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { readVint } from '../tools';
 | 
			
		||||
import { type CreateEbmlBlockTagOptions, EbmlBlockTag } from './tag-block';
 | 
			
		||||
import type { EbmlSimpleBlockTagIdType } from './enums';
 | 
			
		||||
import type { FileDataViewController } from '../adapters';
 | 
			
		||||
import type { DecodeContentOptions } from './tag-trait';
 | 
			
		||||
 | 
			
		||||
export interface CreateEbmlSimpleBlockTagOptions
 | 
			
		||||
  extends Omit<CreateEbmlBlockTagOptions, 'id'> {
 | 
			
		||||
@ -33,12 +33,13 @@ export class EbmlSimpleBlockTag extends EbmlBlockTag {
 | 
			
		||||
    yield this.payload;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async *decodeContentImpl(controller: FileDataViewController) {
 | 
			
		||||
  async *decodeContentImpl(options: DecodeContentOptions) {
 | 
			
		||||
    const controller = options.dataViewController;
 | 
			
		||||
    const offset = controller.getOffset();
 | 
			
		||||
 | 
			
		||||
    const view = await controller.read(offset, this.contentLength, true);
 | 
			
		||||
 | 
			
		||||
    for await (const item of super.decodeContentImpl(controller)) {
 | 
			
		||||
    for await (const item of super.decodeContentImpl(options)) {
 | 
			
		||||
      yield item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import type { EbmlElementType } from './enums';
 | 
			
		||||
import { hexStringToBuf, UNKNOWN_SIZE_VINT_BUF, writeVint } from '../tools';
 | 
			
		||||
import type { FileDataViewController } from '../adapters';
 | 
			
		||||
import { InconsistentOffsetOnDecodingContentError } from '../errors';
 | 
			
		||||
import type { EbmlMasterTag } from './tag-master';
 | 
			
		||||
 | 
			
		||||
export interface CreateEbmlTagOptions {
 | 
			
		||||
  id: EbmlTagIdType;
 | 
			
		||||
@ -16,6 +17,15 @@ export interface CreateEbmlTagOptions {
 | 
			
		||||
  parent?: EbmlTagTrait;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type DecodeContentCollectChildPredicate =
 | 
			
		||||
  | boolean
 | 
			
		||||
  | ((child: EbmlTagTrait, parent: EbmlMasterTag) => boolean);
 | 
			
		||||
 | 
			
		||||
export interface DecodeContentOptions {
 | 
			
		||||
  collectChild?: DecodeContentCollectChildPredicate;
 | 
			
		||||
  dataViewController: FileDataViewController;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export abstract class EbmlTagTrait {
 | 
			
		||||
  /**
 | 
			
		||||
   * The id of the EBML tag.
 | 
			
		||||
@ -117,7 +127,7 @@ export abstract class EbmlTagTrait {
 | 
			
		||||
   * @param controller DataView controller, simulate async filesystem file
 | 
			
		||||
   */
 | 
			
		||||
  protected abstract decodeContentImpl(
 | 
			
		||||
    controller: FileDataViewController
 | 
			
		||||
    options: DecodeContentOptions
 | 
			
		||||
  ): AsyncGenerator<EbmlTagTrait, void, unknown>;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@ -126,13 +136,14 @@ export abstract class EbmlTagTrait {
 | 
			
		||||
   * @returns Deep traversal async iterators of all descendants
 | 
			
		||||
   */
 | 
			
		||||
  public async *decodeContent(
 | 
			
		||||
    controller: FileDataViewController
 | 
			
		||||
    options: DecodeContentOptions
 | 
			
		||||
  ): AsyncGenerator<EbmlTagTrait, void, unknown> {
 | 
			
		||||
    const controller = options.dataViewController;
 | 
			
		||||
    if (this.contentLength === 0 || this.position === EbmlTagPosition.Start) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const startOffset = controller.getOffset();
 | 
			
		||||
    for await (const tag of this.decodeContentImpl(controller)) {
 | 
			
		||||
    for await (const tag of this.decodeContentImpl(options)) {
 | 
			
		||||
      yield tag;
 | 
			
		||||
    }
 | 
			
		||||
    const endOffset = controller.getOffset();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user