Skip to content

Commit

Permalink
v1.8.5 encodeInto()
Browse files Browse the repository at this point in the history
  • Loading branch information
reececomo committed Oct 28, 2024
1 parent 25c4cde commit 1afee21
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 24 deletions.
2 changes: 1 addition & 1 deletion dist/index.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.cjs.map

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ export declare class BufferFormat<EncoderType extends EncoderDefinition, HeaderT
* @see {peekHeaderStr(...)}
*/
header: HeaderType;
get encodingBuffer(): DataView | undefined;
constructor(def: EncoderType, header?: HeaderType | null);
/**
* Read the header of a buffer as a number.
Expand All @@ -248,6 +247,13 @@ export declare class BufferFormat<EncoderType extends EncoderDefinition, HeaderT
* @throws {RangeError} if buffer size < 2
*/
static peekHeaderStr: typeof peekHeaderStr;
/**
* Encode an object into an existing byte array.
*
* **Warning:** Returns an unsafe view into the encoding buffer. Pass this reference to preserve
* performance, and to minimize memory allocation and fragmentation.
*/
encodeInto<TDecodedType extends DecodedType<EncoderType>>(data: TDecodedType, bytes: Uint8Array): Uint8Array;
/**
* Encode an object to bytes.
*
Expand All @@ -263,11 +269,20 @@ export declare class BufferFormat<EncoderType extends EncoderDefinition, HeaderT
* @throws if fails to encode value to schema
*/
encode<TDecodedType extends DecodedType<EncoderType>>(data: TDecodedType, preserveBytes?: boolean): Uint8Array;
/**
* Decode binary data into an existing object instance.
* @throws if fails to decode bytes to schema.
*/
decodeInto<TDecodedType = DecodedType<EncoderType>>(bytes: Uint8Array | ArrayBufferView | ArrayBuffer, obj: Partial<TDecodedType>): TDecodedType;
/**
* Decode binary data to an object.
* @throws if fails to decode bytes to schema.
*/
decode<TDecodedType = DecodedType<EncoderType>>(bytes: Uint8Array | ArrayBufferView | ArrayBuffer, decodeInto?: Partial<TDecodedType>): TDecodedType;
decode<TDecodedType = DecodedType<EncoderType>>(bytes: Uint8Array | ArrayBufferView | ArrayBuffer): TDecodedType;
/**
* @deprecated use decodeInto() instead
*/
decode<TDecodedType = DecodedType<EncoderType>>(bytes: Uint8Array | ArrayBufferView | ArrayBuffer, decodeInto: Partial<TDecodedType>): TDecodedType;
/**
* Set additional transform functions to apply before encoding and after decoding.
*/
Expand Down
2 changes: 1 addition & 1 deletion dist/index.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.mjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tinybuf",
"version": "1.8.3",
"version": "1.8.5",
"author": "Reece Como <[email protected]>",
"authors": [
"Reece Como <[email protected]>",
Expand Down
19 changes: 19 additions & 0 deletions src/__tests__/BufferFormat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,25 @@ describe("BufferFormat", () => {
expect(() => defineFormat("bigint128" as any)).toThrow(TypeError);
});

it("encodeInto()", () => {
const data = new Uint8Array(32);
const input = {
a: 100,
b: [100, 200],
c: [
{ d: undefined },
{ d: "lol" }
]
};

expect(data.toString()).toBe("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
const result = MyBufferFormat.encodeInto(input, data);
expect(data.toString()).not.toBe("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

const reversed = MyBufferFormat.decode(result);
expect(reversed).toStrictEqual(input);
});

it("decode() emits output that is valid input for encode()", () => {
const Example = defineFormat({
integer: Type.UInt16,
Expand Down
52 changes: 47 additions & 5 deletions src/core/BufferFormat.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/unified-signatures */
import { writers, readers } from "./lib/coders";
import * as coders from "./lib/coders";
import { $hashCode, $strToHashCode } from "./lib/hashCode";
Expand Down Expand Up @@ -104,10 +105,6 @@ export class BufferFormat<EncoderType extends EncoderDefinition, HeaderType exte
/** @internal */
private _$writer?: BufferWriter;

public get encodingBuffer(): DataView | undefined {
return this._$writer?._$dataView;
}

public constructor(
def: EncoderType,
header?: HeaderType | null,
Expand Down Expand Up @@ -199,6 +196,27 @@ export class BufferFormat<EncoderType extends EncoderDefinition, HeaderType exte
return new BufferWriter(cfg.encodingBufferInitialSize);
}

/**
* Encode an object into an existing byte array.
*
* **Warning:** Returns an unsafe view into the encoding buffer. Pass this reference to preserve
* performance, and to minimize memory allocation and fragmentation.
*/
public encodeInto<TDecodedType extends DecodedType<EncoderType>>(
data: TDecodedType,
bytes: Uint8Array,
): Uint8Array {
const writer = new BufferWriter(bytes);

if (this._$hasValidationOrTransforms) {
data = this._$preprocess(data);
}

this._$write(data, writer);

return writer.$viewBytes();
}

/**
* Encode an object to bytes.
*
Expand Down Expand Up @@ -236,13 +254,37 @@ export class BufferFormat<EncoderType extends EncoderDefinition, HeaderType exte
: this._$writer.$viewBytes();
}

/**
* Decode binary data into an existing object instance.
* @throws if fails to decode bytes to schema.
*/
public decodeInto<TDecodedType = DecodedType<EncoderType>>(
bytes: Uint8Array | ArrayBufferView | ArrayBuffer,
obj: Partial<TDecodedType>,
): TDecodedType {
return this._$read(new BufferReader(bytes, this.header === undefined ? 0 : 2), obj);
}
/**
* Decode binary data to an object.
* @throws if fails to decode bytes to schema.
*/
public decode<TDecodedType = DecodedType<EncoderType>>(
bytes: Uint8Array | ArrayBufferView | ArrayBuffer
): TDecodedType;
/**
* @deprecated use decodeInto() instead
*/
public decode<TDecodedType = DecodedType<EncoderType>>(
bytes: Uint8Array | ArrayBufferView | ArrayBuffer,
decodeInto: Partial<TDecodedType>,
): TDecodedType;
/**
* Decode binary data to an object.
* @throws if fails to decode bytes to schema.
*/
public decode<TDecodedType = DecodedType<EncoderType>>(
bytes: Uint8Array | ArrayBufferView | ArrayBuffer,
decodeInto?: Partial<TDecodedType>,
decodeInto?: Partial<TDecodedType> | undefined,
): TDecodedType {
return this._$read(new BufferReader(bytes, this.header === undefined ? 0 : 2), decodeInto);
}
Expand Down
30 changes: 18 additions & 12 deletions src/core/lib/BufferWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,26 @@ import { TinybufError } from "./errors";
*/
export class BufferWriter {
public $byteLength: number = 0;
public _$dataView: DataView;
private _$dataView: DataView;
private _$bytes: Uint8Array;
private _$writeHead: number = 0;
private _$resizable: boolean;

public constructor(initialSize: number) {
this._$dataView = new DataView(new ArrayBuffer(initialSize));
public constructor($0: number | Uint8Array) {
this._$resizable = typeof $0 === "number";
let b = $0 instanceof Uint8Array ? $0 : new Uint8Array($0);
this._$bytes = b;
this._$dataView = new DataView(b.buffer, b.byteOffset, b.byteLength);
}

public $viewBytes(): Uint8Array {
return new Uint8Array(this._$dataView.buffer, this._$dataView.byteOffset, this.$byteLength);
return this._$bytes.subarray(this._$bytes.byteOffset, this._$bytes.byteOffset + this.$byteLength);
}

public $copyBytes(): Uint8Array {
return new Uint8Array(this._$dataView.buffer.slice(0, this.$byteLength));
const buf = new Uint8Array(this.$byteLength);
buf.set(this.$viewBytes());
return buf;
}

// ----- Writers: -----
Expand Down Expand Up @@ -81,6 +88,7 @@ export class BufferWriter {
if (this.$byteLength + bytes > this._$dataView.byteLength) {
const minBytesNeeded = this.$byteLength + bytes - this._$dataView.byteLength;
const requestedNewBytes = Math.ceil(minBytesNeeded / cfg.encodingBufferIncrement) * cfg.encodingBufferIncrement;
if (!this._$resizable) throw new TinybufError("exceeded buffer length: " + this._$dataView.byteLength);
this._$resizeBuffer(this._$dataView.byteLength + requestedNewBytes);
}

Expand All @@ -96,13 +104,11 @@ export class BufferWriter {
throw new TinybufError(`exceeded encodingBufferMaxSize: ${cfg.encodingBufferMaxSize}`);
}

const newBuf = new ArrayBuffer(newSize);
const buf = new Uint8Array(newSize);
buf.set(this._$bytes); // copy bytes

// copy bytes
const oldView = new Uint8Array(this._$dataView.buffer, this._$dataView.byteOffset, this._$dataView.byteLength);
new Uint8Array(newBuf).set(oldView);

// update ref
this._$dataView = new DataView(newBuf);
// update refs
this._$dataView = new DataView(buf.buffer);
this._$bytes = buf;
}
}

0 comments on commit 1afee21

Please sign in to comment.