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();
|
||||
|
||||
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