/** * Module definition of {@link PrePost} property and class decorators. * * {@link PrePost} type decorators are used to define functions computed before * and/or after reading or writing the decorated property or class. * * ```mermaid * flowchart TB * subgraph s1[For each properties] * direction TB * PreOperation[__Pre__ property reading operations] --> Condition * click PreOperation "/binspector/modules/PrePost.html" "Documentation for 'Pre' type decorators" * Condition[__Condition__ get the definitive subtype to read based on current state] --> s2 * click Condition "/binspector/modules/Condition.html" "Documentation for 'Condtion' type decorators" * subgraph s2[Reading subtype] * Controller[__Controller__ decides when to stop reading the subtype based on a set of arbitrary conditions] --> TypeReading[Read __Relation__ or __Primitive__] * click Controller "/binspector/modules/Controller.html" "Documentation for 'Controller' type decorators" * click TypeReading "/binspector/modules/Primitive.html" "Documentation for 'Primitive' type decorators" * end * TypeReading --> Controller * s2 --> Transform[__Transform__ the value we read into something else] * click Transform "/binspector/modules/Transformer.html" "Documentation for 'Transformer' type decorators" * Transform --> Validate[__Validate__ the final value] * click Validate "/binspector/modules/Validator.html" "Documentation for 'Validator' type decorators" * Validate --> PostOperation[__Post__ property reading operations] * click PostOperation "/binspector/modules/PrePost.html" "Documentation for 'Post' type decorators" * end * PostOperation --> A@{ shape: framed-circle, label: "Stop" } * style PreOperation fill:blue,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 * style PostOperation fill:blue,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 * ``` * * The {@link PrePost} decorators are used to provide tools to manage cursor * operations, and injecting custom logic into the parsing process. * * - **Offset Management**: Save and restore cursor positions during complex * parsing operations with the {@link Offset} and {@link Peek} decorators. * * - **Endian Handling**: Dynamically set and restore endianness for properties, * classes, or subtypes with the {@link Endian} decorator. * * - **Debugging and Analysis**: Use the {@link Pre} and {@link Post} decorators * to log cursor positions or values during parsing. * * - **Dynamic Behavior**: Implement custom functions that allows developper to * not be constrained by the declarative nature of the library. * * @module PrePost */ import { ClassMetaDescriptor, NumberOrRecursiveKey, type PropertyMetaDescriptor } from './common.ts'; import { ExecutionScope, type ClassAndPropertyDecoratorType, type ClassAndPropertyDecoratorContext, type DecoratorType } from '../types.ts'; import { type Cursor, BinaryCursorEndianness } from '../cursor.ts'; export declare const PreFunctionSymbol: unique symbol; export declare const PostFunctionSymbol: unique symbol; export declare const PreClassFunctionSymbol: unique symbol; export declare const PostClassFunctionSymbol: unique symbol; export type PrePostSymbols = typeof PreFunctionSymbol | typeof PostFunctionSymbol | typeof PreClassFunctionSymbol | typeof PostClassFunctionSymbol; export type PrePostFunction = (instance: This, cursor: Cursor) => void; /** * PrePostOptions. * * @category Options */ export interface PrePostOptions { /** * Ensures that a relation exists before defining the PrePost decorator. */ primitiveCheck: boolean; /** * Removes the decorator from metadata after its function is executed. */ once: boolean; /** * Specifies whether the prepost function should be executed during * the read phase, the write phase, or both. */ scope: ExecutionScope; } /** * @category Options */ export declare const PrePostOptionsDefault: { primitiveCheck: boolean; once: boolean; scope: ExecutionScope; }; /** * PrePost type interface structure definition. * * @extends {PropertyMetaDescriptor} */ export interface PrePost extends PropertyMetaDescriptor { /** * Options for prepost decorator */ options: PrePostOptions; /** * Function that will be executed before or after the Controller, Validator and Transformer decorator. */ func: PrePostFunction; } export interface PrePostClass extends ClassMetaDescriptor { /** * Options for prepost decorator */ options: PrePostOptions; /** * Function to be executed before or after the Controller, Validator, and Transformer decorators. */ func: PrePostFunction; } /** * `preFunctionDecoratorFactory` function helps creates a decorator that saves * the metadata of `Pre` type decorator. * * @typeParam This The type of the class the decorator is applied to. * * @param {string} name Name of the 'Pre' type decorator. * * @param {PrePostFunction} func Function that will be executed before the * property or class has been processed, that receive the instance and cursor * value as argument. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Advanced Use */ export declare function preFunctionDecoratorFactory(name: string, func: PrePostFunction, opt?: Partial): ClassAndPropertyDecoratorType; /** * `postFunctionDecoratorFactory` function helps creates a decorator that saves * the metadata of the `Post` type decorator. * * @typeParam This The type of the class the decorator is applied to. * * @param {string} name Name of the 'Post' type decorator. * @param {PrePostFunction} func Function that will be executed after the property or class has been fully processed. * @param {Partial} opt PrePost options. * @returns {DecoratorType} The class or property decorator function. * * @category Advanced Use */ export declare function postFunctionDecoratorFactory(name: string, func: PrePostFunction, opt?: Partial): ClassAndPropertyDecoratorType; /** * `@Pre` decorator defines a function computed before reading or * writing the value of the decorated property or class . * * It is typically used for pre-processing tasks such as debugging, * to run operation on the Cursor instance at runtime. * * @example * * In the following example, the `@Pre` operator is used read the value * of the cursor and make a debug log to notify the position where the * type definition start to be read. * * ```typescript * @Pre((_, cursor) => { console.log(`${_.constructor.name} : ${cursor.offset()}`) }) * class Protocol { * @Pre((_, cursor) => { console.log(`${_.constructor.name} : ${cursor.offset()}`) }) * @Uint8 * foo: number * } * ``` * * @typeParam This The type of the class the decorator is applied to. * * @param {PrePostFunction} func Function that will be executed before the * property or class has been processed, that receive the instance and cursor * value as argument. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function Pre(func: PrePostFunction, opt?: Partial): ClassAndPropertyDecoratorType; /** * `@Post` decorator defines a function computed after fully reading or * writing the value of the decorated property or class . * * It is typically used for post-processing tasks such as debugging, or * to enforce custom constraints that the library cannot handle declaratively. * * @example * * In the following example, the `@Post` operator is used to store the value of * the property it decorate inside a global variable that will be accessed by other * part of the code. Currently, the library does not natively support shared * context functionality, this example demonstrates a workaround for achieving * similar behavior. * * ```typescript * const GLOBAL_STORAGE = {} * * // After the 'Record' class has been processed key-value will be stored * // in the 'GLOBAL_STORAGE' variable. * @Post(_ => { GLOBAL_STORAGE[_.key] = _.value }) * class Record { * @NullTerminatedString() * key: string * * @NullTerminatedString() * value: string * } * * class SubProtocol { * ... * constructor (key: string, value: string) { * ... * } * } * * class Protocol { * @Count(16) * @Relation(Records) * records: Record * * // Map 'GLOBAL_STORAGE' to a [key, value] array and pass it to the * // SubProtocol constructor. * @MapTo(_ => Object.entries(GLOBAL_STORAGE)) * @Relation(SubProtocol) * map: * } * ``` * * @typeParam This The type of the class the decorator is applied to. * * @param {PrePostFunction} func Function that will be executed after the property or class has been fully processed. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function Post(func: PrePostFunction, opt?: Partial): ClassAndPropertyDecoratorType; /** * `@Offset` decorator define the address where the cursor should move to * continue the next reading or writing operation. * * This is useful when dealing with binary file format with sections of data * located at specific address. * * @example * * Some binary file format will define arbitrary area where part its definition * is stored based on an address referenced somewhere else. * * In the following example, the `@Offset` decorator moves the cursor to an * address defined in the 'header.address' property and then read * null-terminated strings until the enf of the file. * * ```typescript * class ProtocolHeader { * @Uint32 * address: number * } * * class Protocol { * @Relation(ProtocolHeader) * header: ProtocolHeader * * @Offset('header.address') // Move the cursor the the address in the header * @Until(EOF) // Keep reading strings until the end of the file * @NullTerminatedString() // Read a null-terminated string * area: string[] * } * ``` * @typeParam This The type of the class the decorator is applied to. * * @param {NumberOrRecursiveKey | (instance: This, cursor: Cursor) => number} offset Arbitrary position in the file to move the cursor * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function Offset(offset: NumberOrRecursiveKey | ((instance: This, cursor: Cursor) => number), opt?: Partial): ClassAndPropertyDecoratorType; /** * `@Peek` decorator moves the cursor to a specified address to read/write the * decorated property, then moves back the cursor to its original position. * * The `@Peek` decorator shares similar functionality with the {@link Offset} * decorator, but with a key difference: `@Peek` will automatically reset the * cursor to its original position after reading or writing the decorated * class or property. * * @example * * A use case where the `@Peek` decorator could be used instead of `@Offset` * is if you need to apply some form of processing to the value of the property * to know decide on the structure that value follows. * * In the following example the structure of the bitfield is only known based * on the value of the most significant bit. For this I use `@Peek` to check the * content of the next value and then I properly read it in the correct form. * * ```typescript * class BitfieldB { * ... * } * * class BitfieldA { * ... * } * * class Protocol { * // Use `@Peek` to check the MSB but restore the cursor position * @Peek() * @Transform(x => x & 0x80) * @Uint8 * peeked: number * * @IfThen(_ => _.peeked > 0, BitfieldA) * @Else(BitfieldB) * bitfield: BitfieldA | BitfieldB * } * ``` * * You can also use the `@Peek` decorator with a dynamic offset that depends on * the class instance and the current cursor position: * * ```typescript * class Protocol { * @Uint8 * offset: number * * @Peek((instance, cursor) => cursor.offset() + instance.offset) * @Uint8 * peeked: number * } * ``` * * @remarks * * If you don’t need to return the cursor to its original position or know * the exact position of the next read/write operation, use `@Offset`. * * @typeParam This The type of the class the decorator is applied to. * * @param {NumberOrRecursiveKey | ((instance: This, cursor: Cursor) => number)} [offset] * The offset to move the cursor to before reading or writing the decorated property. It can be: * - A static number, indicating a fixed offset. * - A string that refer to a property of the current instance. * - A function that computes the offset dynamically based on the current instance and cursor. * - Undefined, the cursor will be set back to its original position after read/write. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function Peek(offset?: NumberOrRecursiveKey | ((instance: This, cursor: Cursor) => number), opt?: Partial): ClassAndPropertyDecoratorType; /** * `@EnsureSize` decorator force the decorated property to meet a size * constraint. If that size is not met the cursor will be moved. * * @example * * In the following example the structure of the `Block` is fixed based on * a property known at runtime. * The `@EnsureSize` decorator move the cursor to always move the cursor * to the correct position after read or write. * * ```typescript * @EnsureSize('_size') * class Block { * @NullTerminatedString() * content: string * * constructor(public _size: number) {} * } * * class Protocol { * @Uint16 * block_size: number * * @Uint32 * block_count: number * * @Count('block_count') * @Relation(Block, 'block_size') * blocks: Block[] * } * ``` * * @typeParam This The type of the class the decorator is applied to. * * @param {NumberOrRecursiveKey | ((instance: This, cursor: Cursor) => number)} size * The size of the decorated property or class. * - A static number, indicating a fixed size. * - A string that refer to a property of type number. * - A function that computes the offset dynamically based on the current instance and cursor. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function EnsureSize(size: NumberOrRecursiveKey | ((instance: This, cursor: Cursor) => number), opt?: Partial): ClassAndPropertyDecoratorType; /** * `@Endian` decorator change the endianness dynamically of the decorated * property or class and then set it back to the previous endianness once * the decorated property/class has been fully processed. * * @example * * **Changing Endianness for individual properties** * * Use the `@Endian` decorator over a `PrimitiveSymbol` property to change it's endianness. * * ```typescript * class Protocol { * @Endian(BinaryCursorEndianness.LittleEndian) * @Uint16 * little_endian: number * * @Uint32 * big_endian: number * } * ``` * * **Changing Endianness for subtypes** * * By using the `@Endian` decorator over a subtype relation property to change * the endianness of the entire sub-type * * ```typescript * class SubProtocol { * @Uint32 * foo: number * * @Uint32 * bar: number * } * * class Protocol { * @Endian(BinaryCursorEndianness.LittleEndian) * @Relation(SubProtocol) * sub_type: SubProtocol * * @Uint32 * big_endian: number // Because the default endianness is big endian this value will be read as a big endian number * } * ``` * * **Runtime Conditional Endianness:** * * If you need to set the endianness dynamically at runtime based on a class * property, you can pass a function to the `@Endian` decorator. * * In the following example, the `@Endian` decorator uses the value of the * `_value` property passed as an argument of `Protocol` constructor whether * little-endian or big-endian byte order should be applied to the protocol. * * ```typescript * @Endian(_ => _value > 0 ? BinaryCursorEndianness.LittleEndian : BinaryCursorEndianness.BigEndian) * class Protocol { * _value: number * * @Uint32 * foo: number * * @Uint32 * bar: number * * constructor(value: number) { * this._value = value * } * } * ``` * * @remarks * * - The `@Endian` decorator is used to change endianness dynamically or based * on values known at runtime. If you want to describe the endianness of a * protocol that won't change use {@link LittleEndian} and {@link BigEndian}. * * - Because of the current architecture of the library, if multiple `@Endian` * that are based on values known at runtime are run in parallel it could * potentially mess up the result because they will all populate the 'post' * property metadata. * * @typeParam This The type of the class the decorator is applied to. * * @param {BinaryCursorEndianness | ((instance: This) => BinaryCursorEndianness)} endianness The endianness to apply, or a function that return the endianness based on the instance value. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @see {@link LittleEndian} * @see {@link BigEndian} * * @category Decorators */ export declare function Endian(endianness: BinaryCursorEndianness | ((instance: This) => BinaryCursorEndianness), opt?: Partial): ClassAndPropertyDecoratorType; /** * `@LittleEndian` decorator set the endianness of a decorated class or * property to little endian. * * @example * * **Set the endianness of an entire type definition** * * Use the `@LittleEndian` decorator on top of a class * declaration to change the endianness of all its properties. * * In the following example, the `Protocol` type definition that * include two unsigned 32bits integer: `foo` and `bar`, will use little-endian * byte order. * * ```typescript * @LittleEndian * class Protocol { * @Uint32 * foo: number * * @Uint32 * bar: number * } * ``` * * @remarks * * - The difference with {@link Endian} is that this decorator is not dynamic. * The endianness won't be set back to it's previous value after the * decorated class/property is read. * * - When working with recursive binary definition be sure to use this decorator * instead of {@link Endian}. * * @typeParam This The type of the class the decorator is applied to. * * @see {@link BigEndian} * @see {@link Endian} * * @category Decorators */ export declare function LittleEndian(_: any, context: ClassAndPropertyDecoratorContext): void; /** * `@BigEndian` decorator set the endianness of a decorated class or * property to big endian. * * @example * * **Set the endianness of an entire type definition** * * Use the `@BigEndian` decorator on top of a class * declaration to change the endianness of all its properties. * * In the following example, the `Protocol` type definition that * include two unsigned 32bits integer: `foo` and `bar`, will use big-endian * byte order. * * ```typescript * @BigEndian * class Protocol { * @Uint32 * foo: number * * @Uint32 * bar: number * } * ``` * * @remarks * * - The difference with {@link Endian} is that this decorator is not dynamic. * The endianness won't be set back to it's previous value after the * decorated class/property is read. * * - When working with recursive binary definition be sure to use this decorator * instead of {@link Endian}. * * @typeParam This The type of the class the decorator is applied to. * * @see {@link BigEndian} * @see {@link Endian} * * @category Decorators */ export declare function BigEndian(_: any, context: ClassAndPropertyDecoratorContext): void; type ValueSetFunction = (instance: This) => Value; /** * `@ValueSet` decorator set the value of the decorated property based on * a function passed as a parameter. * This decorator don't read anything from the binary file and is just used * to add more context to a class during the reading. * * @example * * In the following example `@ValueSet` is used to fetch the protocol type name * based on an id read in by the binary definition. * The `protocol_name` will just appear when the object is serialized and will * gave the object more context. * * ```typescript * const ID_TO_NAME = { * 1: "Record", * ... * } * * class Protocol { * @Uint8 * protocol_id: number * * @ValueSet(_ => ID_TO_NAME[_.protocol_id] || 'UNKNOWN') * protocol_name: string * } * ``` * * @typeParam This The type of the class the decorator is applied to. * * @param {ValueSetFunction} setter Function that will store the return value in the decorated property. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @throws {@link Primitive.RelationAlreadyDefinedError} if a relation metadata is found. */ export declare function ValueSet(setter: ValueSetFunction, opt?: Partial): DecoratorType; /** * `@Padding` ensures that the total size of the decorated property aligns to * a multiple of the specified padding value. After reading or writing the * property, the cursor is advanced forward if necessary to maintain alignment. * * @example * * In the following example the `data` property is padded so the length (after * read or write) is a multiple of 4. * * ```typescript * class Protocol { * @Uint32 * length: number * * @Padding(4) * @Count('length') * @Uint8 * data: number[] * } * ``` * * @param {number} padding The total size of the decorated property will be * adjusted to be a multiple of this value. * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function Padding(padding: number, opt?: Partial): ClassAndPropertyDecoratorType; /** * `@SharePropertiesWithRelation` assigns all properties from the current * instance to the associated relation instance. * * @remarks * * You should probably not use this decorator as it will break typescript type * system. * If you need to pass information to the associated relation please use * constructors. * This decorator has in the context of re-writting the `Matrix` decorator please * see the implementation of that decorator to understand the use-case of this. * * @param {Partial} [opt] Optional configution. * @returns {DecoratorType} The class or property decorator function. * * @category Decorators */ export declare function SharePropertiesWithRelation(opt?: Partial): DecoratorType; /** * usePrePost execute an array of `PrePost` decorator metadata on a target instance. * * @param {Array} prepost Array of `PrePost` decorator metadatas. * @param {T} targetInstance Current state of the object the `PrePost` decorators are defined in, that will be passed to the PrePost decorator functions. * @param {Cursor} cursor Cursor state that will be passed to the PrePost decorator functions. * @returns {void} * * @category Advanced Use */ export declare function usePrePost(prepost: Array> | Array>, targetInstance: This, cursor: Cursor, scope?: ExecutionScope): void; export {};