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