New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

buffer-backed-object

Package Overview
Dependencies
Maintainers
2
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

buffer-backed-object - npm Package Compare versions

Comparing version 0.3.1 to 1.0.0

.github/workflow

538

buffer-backed-object.test.js

@@ -1,246 +0,344 @@

/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, test } from "vitest";
import {
ArrayOfBufferBackedObjects,
BufferBackedObject,
structSize,
} from "./buffer-backed-object.js";
import * as BBO from "./buffer-backed-object.ts";
describe("structSize", function () {
it("calculates the size of a struct correctly", function () {
const size = structSize({
id: BufferBackedObject.Uint8(),
x: BufferBackedObject.Float64(),
y: BufferBackedObject.Int16(),
z: BufferBackedObject.BigUint64(),
_: BufferBackedObject.reserved(1),
test("structSize calculates the size of a struct correctly", function () {
{
const size = BBO.structSize({
id: BBO.Uint8(),
});
expect(size).toBe(20);
});
});
expect(size).toBe(1);
}
describe("ArrayOfBufferBackedObjects", function () {
it("calculates length correctly", function () {
// Add one stray byte
const { buffer } = new Uint8Array([0, 0, 1, 0, 2, 0, 1]);
const aosv = new ArrayOfBufferBackedObjects(buffer, {
id: BufferBackedObject.Uint8(),
_: BufferBackedObject.reserved(1),
{
const size = BBO.structSize({
id: BBO.Uint16(),
});
expect(aosv.length).toBe(3);
});
expect(size).toBe(2);
}
it("decodes items correctly", function () {
const descriptor = {
id: BufferBackedObject.Uint8(),
x: BufferBackedObject.Float64({ endianness: "big" }),
y: BufferBackedObject.Float64({ endianness: "little" }),
texture: BufferBackedObject.Int32(),
_: BufferBackedObject.reserved(1),
};
{
const size = BBO.structSize({
id: BBO.Uint16(),
id2: BBO.Uint8(),
});
expect(size).toBe(4);
}
const buffer = new ArrayBuffer(structSize(descriptor) * 2);
const dataView = new DataView(buffer);
dataView.setUint8(0 + 0, 1);
dataView.setUint8(22 + 0, 2);
dataView.setFloat64(0 + 1, 20, false);
dataView.setFloat64(0 + 9, 30, true);
dataView.setFloat64(22 + 1, 40, false);
dataView.setFloat64(22 + 9, 50, true);
dataView.setInt32(0 + 17, 9, true);
dataView.setInt32(22 + 17, 10, true);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptor);
expect(aosv[0].id).toBe(1);
expect(aosv[1].id).toBe(2);
expect(aosv[0].x).toBe(20);
expect(aosv[1].x).toBe(40);
expect(aosv[0].y).toBe(30);
expect(aosv[1].y).toBe(50);
expect(aosv[0].texture).toBe(9);
expect(aosv[1].texture).toBe(10);
});
{
const size = BBO.structSize({
id: BBO.Uint8(),
id2: BBO.Uint16(),
});
expect(size).toBe(4);
}
it("can have an offset", function () {
const descriptor = {
id: BufferBackedObject.Uint8(),
_: BufferBackedObject.reserved(1),
};
const buffer = new ArrayBuffer(structSize(descriptor) * 4 + 1);
const dataView = new DataView(buffer);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptor, {
byteOffset: 1,
length: 2,
{
const size = BBO.structSize({
id: BBO.Uint8(),
id2: BBO.Uint32(),
id3: BBO.Uint32(),
});
expect(aosv.length).toBe(2);
aosv[0].id = 1;
aosv[1].id = 1;
expect(dataView.getUint8(1)).toBe(1);
});
expect(size).toBe(12);
}
it("handles nested Array of BBOs", function () {
const descriptors = {
id: BufferBackedObject.Uint8(),
vertices: BufferBackedObject.NestedArrayOfBufferBackedObjects(3, {
x: BufferBackedObject.Float64(),
y: BufferBackedObject.Float64(),
}),
};
const buffer = new ArrayBuffer(structSize(descriptors) * 3);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptors);
expect(aosv.length).toBe(3);
aosv[2].id = 1;
aosv[2].vertices[0].x = 0;
aosv[2].vertices[0].y = 1;
aosv[2].vertices[1].x = 2;
aosv[2].vertices[1].y = 3;
aosv[2].vertices[2].x = 4;
aosv[2].vertices[2].y = 5;
expect(aosv.length).toBe(3);
expect(JSON.stringify(aosv[2])).toBe(
JSON.stringify({
id: 1,
vertices: [
{ x: 0, y: 1 },
{ x: 2, y: 3 },
{ x: 4, y: 5 },
],
})
);
});
{
const size = BBO.structSize({
id: BBO.Uint8(), // 0...7
x: BBO.Float64({ endianness: "big" }), // 8...15
y: BBO.Float64({ endianness: "little" }), // 16...23
texture: BBO.Int32(), // 24...28
_: BBO.reserved(1), // 28...29
});
expect(size).toBe(32);
}
});
it("handles nested BBO", function () {
const descriptors = {
id: BufferBackedObject.Uint8(),
pos: BufferBackedObject.NestedBufferBackedObject({
x: BufferBackedObject.Float64(),
y: BufferBackedObject.Float64(),
}),
};
const buffer = new ArrayBuffer(structSize(descriptors) * 3);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptors);
test("ArrayOfBufferBackedObjects calculates length correctly", function () {
{
const buffer = new ArrayBuffer(7);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, {
id: BBO.Uint8(),
_: BBO.reserved(1),
});
expect(aosv.length).toBe(3);
aosv[2].id = 1;
aosv[2].pos.x = 3;
aosv[2].pos.y = 2;
expect(aosv.length).toBe(3);
expect(JSON.stringify(aosv[2])).toBe(
JSON.stringify({ id: 1, pos: { x: 3, y: 2 } })
);
expect(JSON.stringify(aosv)).toBe(
JSON.stringify([
{ id: 0, pos: { x: 0, y: 0 } },
{ id: 0, pos: { x: 0, y: 0 } },
{ id: 1, pos: { x: 3, y: 2 } },
])
);
});
}
it("can return the buffer", function () {
const buffer = new ArrayBuffer(22);
const aosv = new ArrayOfBufferBackedObjects(buffer, {
x: BufferBackedObject.Uint8(),
{
const buffer = new ArrayBuffer(47);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, {
id: BBO.Uint8(),
i2: BBO.BigInt64(),
});
expect(aosv.buffer).toBe(buffer);
});
expect(aosv.length).toBe(2);
}
});
it("encodes to JSON", function () {
const { buffer } = new Uint8Array([0, 0, 1, 0, 2, 0, 1]);
const aosv = new ArrayOfBufferBackedObjects(buffer, {
id: BufferBackedObject.Uint8(),
_: BufferBackedObject.reserved(1),
});
expect(JSON.stringify(aosv)).toBe(
JSON.stringify([{ id: 0 }, { id: 1 }, { id: 2 }])
);
});
test("ArrayOfBufferBackedObjects decodes items correctly", function () {
const descriptor = {
id: BBO.Uint8(), // 0...7
x: BBO.Float64({ endianness: "big" }), // 8...15
y: BBO.Float64({ endianness: "little" }), // 16...23
texture: BBO.Int32(), // 24...27
_: BBO.reserved(1), // 28...29
};
it("can write items", function () {
const descriptor = {
id: BufferBackedObject.Uint8(),
x: BufferBackedObject.Float64(),
_: BufferBackedObject.reserved(1),
};
const buffer = new ArrayBuffer(structSize(descriptor) * 2);
const dataView = new DataView(buffer);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptor);
aosv[0].x = 10;
aosv[1].x = 20;
expect(dataView.getFloat64(1, true)).toBe(10);
expect(dataView.getFloat64(11, true)).toBe(20);
});
console.log(BBO.structSize(descriptor), BBO.structAlign(descriptor));
const buffer = new ArrayBuffer(BBO.structSize(descriptor) * 2);
const dataView = new DataView(buffer);
dataView.setUint8(0 + 0, 1);
dataView.setUint8(32 + 0, 2);
dataView.setFloat64(0 + 8, 20, false);
dataView.setFloat64(0 + 16, 30, true);
dataView.setFloat64(32 + 8, 40, false);
dataView.setFloat64(32 + 16, 50, true);
dataView.setInt32(0 + 24, 9, true);
dataView.setInt32(32 + 24, 10, true);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptor);
expect(aosv[0].id).toBe(1);
expect(aosv[1].id).toBe(2);
expect(aosv[0].x).toBe(20);
expect(aosv[1].x).toBe(40);
expect(aosv[0].y).toBe(30);
expect(aosv[1].y).toBe(50);
expect(aosv[0].texture).toBe(9);
expect(aosv[1].texture).toBe(10);
});
it("handles filter()", function () {
const descriptor = {
id: BufferBackedObject.Uint8(),
data: BufferBackedObject.Uint8(),
};
const { buffer } = new Uint8Array([0, 10, 1, 11, 2, 12, 3, 13]);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptor);
const even = aosv.filter(({ id }) => id % 2 == 0);
expect(even.length).toBe(2);
even[1].data = 99;
expect(aosv[2].data).toBe(99);
test("ArrayOfBufferBackedObjects can have an offset", function () {
const descriptor = {
id: BBO.Uint8(),
_: BBO.reserved(1),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptor) * 4 + 1);
const dataView = new DataView(buffer);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptor, {
byteOffset: 1,
length: 2,
});
expect(aosv.length).toBe(2);
aosv[0].id = 1;
aosv[1].id = 1;
expect(dataView.getUint8(1)).toBe(1);
});
it("rejects new properties", function () {
const descriptor = {
id: BufferBackedObject.Uint8(),
data: BufferBackedObject.Uint8(),
};
const { buffer } = new Uint8Array([0, 10, 1, 11, 2, 12, 3, 13]);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptor);
expect(() => {
aosv[0].lol = 4;
}).toThrow();
test("ArrayOfBufferBackedObjects handles nested Array of BBOs", function () {
const descriptors = {
id: BBO.Uint8(),
vertices: BBO.NestedArrayOfBufferBackedObjects(3, {
x: BBO.Float64(),
y: BBO.Float64(),
}),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptors) * 3);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptors);
expect(aosv.length).toBe(3);
aosv[2].id = 1;
aosv[2].vertices[0].x = 0;
aosv[2].vertices[0].y = 1;
aosv[2].vertices[1].x = 2;
aosv[2].vertices[1].y = 3;
aosv[2].vertices[2].x = 4;
aosv[2].vertices[2].y = 5;
expect(aosv.length).toBe(3);
expect(JSON.stringify(aosv[2])).toBe(
JSON.stringify({
id: 1,
vertices: [
{ x: 0, y: 1 },
{ x: 2, y: 3 },
{ x: 4, y: 5 },
],
})
);
});
test("ArrayOfBufferBackedObjects handles nested BBO", function () {
const descriptors = {
id: BBO.Uint8(),
pos: BBO.NestedBufferBackedObject({
x: BBO.Float64(),
y: BBO.Float64(),
}),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptors) * 3);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptors);
expect(aosv.length).toBe(3);
aosv[2].id = 1;
aosv[2].pos.x = 3;
aosv[2].pos.y = 2;
expect(aosv.length).toBe(3);
expect(JSON.stringify(aosv[2])).toBe(
JSON.stringify({ id: 1, pos: { x: 3, y: 2 } })
);
expect(JSON.stringify(aosv)).toBe(
JSON.stringify([
{ id: 0, pos: { x: 0, y: 0 } },
{ id: 0, pos: { x: 0, y: 0 } },
{ id: 1, pos: { x: 3, y: 2 } },
])
);
});
test("ArrayOfBufferBackedObjects handles nested BBO with nested arrays", function () {
const PathNodeDescription = {
type: BBO.Uint8(),
x: BBO.Uint32(),
y: BBO.Uint32(),
};
const EnemyDescription = {
type: BBO.Uint8(),
x: BBO.Float32(),
y: BBO.Float32(),
path: BBO.NestedArrayOfBufferBackedObjects(1, PathNodeDescription),
};
const GameStateDescription = {
gametime: BBO.Uint32(),
season: BBO.Uint8(),
enemies: BBO.NestedArrayOfBufferBackedObjects(1, EnemyDescription),
};
const gameStateBuffer = new ArrayBuffer(BBO.structSize(GameStateDescription));
const gameState = BBO.BufferBackedObject(
gameStateBuffer,
GameStateDescription
);
gameState.gametime = 12345;
gameState.season = 3;
expect(gameState.enemies.length).toBe(1);
gameState.enemies[0].type = 1;
gameState.enemies[0].x = 512;
gameState.enemies[0].y = 0;
expect(gameState.enemies.length).toBe(1);
expect(JSON.stringify(gameState.enemies[0])).toBe(
JSON.stringify({ type: 1, x: 512, y: 0, path: [{ type: 0, x: 0, y: 0 }] })
);
expect(JSON.stringify(gameState)).toBe(
JSON.stringify({
gametime: 12345,
season: 3,
enemies: [{ type: 1, x: 512, y: 0, path: [{ type: 0, x: 0, y: 0 }] }],
})
);
});
test("ArrayOfBufferBackedObjects can return the buffer", function () {
const buffer = new ArrayBuffer(22);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, {
x: BBO.Uint8(),
});
expect(aosv.buffer).toBe(buffer);
});
it("can handle UTF8 strings", function () {
const descriptor = {
name: BufferBackedObject.UTF8String(32),
id: BufferBackedObject.Uint8(),
};
const buffer = new ArrayBuffer(structSize(descriptor) * 2);
const aosv = new ArrayOfBufferBackedObjects(buffer, descriptor);
aosv[0].name = "Surma";
aosv[1].name = "Jason";
const name1 = new TextDecoder().decode(new Uint8Array(buffer, 0, 5));
const name2 = new TextDecoder().decode(new Uint8Array(buffer, 33, 5));
expect(name1).toBe("Surma");
expect(name2).toBe("Jason");
test("ArrayOfBufferBackedObjects encodes to JSON", function () {
const { buffer } = new Uint8Array([0, 0, 1, 0, 2, 0, 1]);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, {
id: BBO.Uint8(),
_: BBO.reserved(1),
});
expect(JSON.stringify(aosv)).toBe(
JSON.stringify([{ id: 0 }, { id: 1 }, { id: 2 }])
);
});
describe("StructuredDataView", function () {
it("decodes items correctly", function () {
const descriptor = {
id: BufferBackedObject.Uint8(),
x: BufferBackedObject.Float64({ endianness: "big" }),
y: BufferBackedObject.Float64({ endianness: "little" }),
texture: BufferBackedObject.Int32(),
_: BufferBackedObject.reserved(1),
};
test("ArrayOfBufferBackedObjects can write items", function () {
const descriptor = {
id: BBO.Uint8(),
x: BBO.Float64(),
_: BBO.reserved(1),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptor) * 2);
const dataView = new DataView(buffer);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptor);
aosv[0].x = 10;
aosv[1].x = 20;
expect(dataView.getFloat64(8, true)).toBe(10);
expect(dataView.getFloat64(32, true)).toBe(20);
});
const buffer = new ArrayBuffer(structSize(descriptor));
const dataView = new DataView(buffer);
dataView.setUint8(0 + 0, 1);
dataView.setFloat64(0 + 1, 20, false);
dataView.setFloat64(0 + 9, 30, true);
dataView.setInt32(0 + 17, 9, true);
const sdv = new BufferBackedObject(buffer, descriptor);
expect(sdv.id).toBe(1);
expect(sdv.x).toBe(20);
expect(sdv.y).toBe(30);
expect(sdv.texture).toBe(9);
});
test("ArrayOfBufferBackedObjects handles filter()", function () {
const descriptor = {
id: BBO.Uint8(),
data: BBO.Uint8(),
};
const { buffer } = new Uint8Array([0, 10, 1, 11, 2, 12, 3, 13]);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptor);
const even = aosv.filter(({ id }) => id % 2 == 0);
expect(even.length).toBe(2);
even[1].data = 99;
expect(aosv[2].data).toBe(99);
});
test("ArrayOfBufferBackedObjects rejects new properties", function () {
const descriptor = {
id: BBO.Uint8(),
data: BBO.Uint8(),
};
const { buffer } = new Uint8Array([0, 10, 1, 11, 2, 12, 3, 13]);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptor);
expect(() => {
aosv[0].lol = 4;
}).toThrow();
});
test("ArrayOfBufferBackedObjects can handle UTF8 strings", function () {
const descriptor = {
name: BBO.UTF8String(32),
id: BBO.Uint8(),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptor) * 2);
const aosv = BBO.ArrayOfBufferBackedObjects(buffer, descriptor);
aosv[0].name = "Surma";
aosv[1].name = "Jason";
const name1 = new TextDecoder().decode(new Uint8Array(buffer, 0, 5));
const name2 = new TextDecoder().decode(new Uint8Array(buffer, 33, 5));
expect(name1).toBe("Surma");
expect(name2).toBe("Jason");
});
test("BufferBackedObject decodes items correctly", function () {
const descriptor = {
id: BBO.Uint8(),
x: BBO.Float64({ endianness: "big" }),
y: BBO.Float64({ endianness: "little" }),
texture: BBO.Int32(),
_: BBO.reserved(1),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptor));
const dataView = new DataView(buffer);
dataView.setUint8(0 + 0, 1);
dataView.setFloat64(0 + 8, 20, false);
dataView.setFloat64(0 + 16, 30, true);
dataView.setInt32(0 + 24, 9, true);
const sdv = BBO.BufferBackedObject(buffer, descriptor);
expect(sdv.id).toBe(1);
expect(sdv.x).toBe(20);
expect(sdv.y).toBe(30);
expect(sdv.texture).toBe(9);
});
test("BufferBackedObject decodes items correctly with custom align", function () {
const descriptor = {
id: BBO.Uint8(),
x: BBO.Float64({ endianness: "big", align: 1 }),
y: BBO.Float64({ endianness: "little", align: 1 }),
texture: BBO.Int32({ align: 1 }),
_: BBO.reserved(1),
};
const buffer = new ArrayBuffer(BBO.structSize(descriptor));
const dataView = new DataView(buffer);
dataView.setUint8(0 + 0, 1);
dataView.setFloat64(0 + 1, 20, false);
dataView.setFloat64(0 + 9, 30, true);
dataView.setInt32(0 + 17, 9, true);
const sdv = BBO.BufferBackedObject(buffer, descriptor);
expect(sdv.id).toBe(1);
expect(sdv.x).toBe(20);
expect(sdv.y).toBe(30);
expect(sdv.texture).toBe(9);
});

@@ -1,14 +0,13 @@

import {assert, IsExact} from "conditional-type-checks";
import { assert, IsExact } from "conditional-type-checks";
import {BufferBackedObject, ArrayOfBufferBackedObjects} from ".";
import * as BBO from "./buffer-backed-object.js";
const view = new BufferBackedObject(null as any, {
id: BufferBackedObject.Uint16({ endianness: "little" }),
name: BufferBackedObject.UTF8String(32),
data: BufferBackedObject.ArrayBuffer(100),
position: BufferBackedObject.NestedBufferBackedObject({
x: BufferBackedObject.Float64(),
y: BufferBackedObject.Float64(),
z: BufferBackedObject.Float64(),
})
const view = BBO.BufferBackedObject(null as any, {
id: BBO.Uint16({ endianness: "little" }),
name: BBO.UTF8String(32),
position: BBO.NestedBufferBackedObject({
x: BBO.Float64(),
y: BBO.Float64(),
z: BBO.Float64(),
}),
});

@@ -18,11 +17,10 @@

assert<IsExact<typeof view.name, string>>(true);
assert<IsExact<typeof view.data, ArrayBuffer>>(true);
assert<IsExact<typeof view.position.x, number>>(true);
const descriptors = {
id: BufferBackedObject.Uint16({ endianness: "little" }),
name: BufferBackedObject.UTF8String(32),
id: BBO.Uint16({ endianness: "little" }),
name: BBO.UTF8String(32),
};
const view2 = new ArrayOfBufferBackedObjects(null as any, descriptors);
const view2 = BBO.ArrayOfBufferBackedObjects(null as any, descriptors);
assert<IsExact<typeof view2, Array<{id: number, name: string}>>>(true);
assert<IsExact<typeof view2, Array<{ id: number; name: string }>>>(true);

@@ -1,79 +0,45 @@

/**
* Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type Descriptor<T = any> = {
size: number;
get(dataview: DataView, byteOffset: number): T;
set(dataview: DataView, byteOffset: number, value: T): void;
};
export type Descriptors = {
[key: string]: Descriptor<any>;
};
export type DecodedBuffer<E extends Descriptors> = {
[K in keyof E]: ReturnType<E[K]["get"]>;
};
export function structSize(descriptors: Descriptors): number;
export interface BufferBackedObjectOptions {
byteOffset?: number;
declare module "buffer-backed-object" {
export type Descriptor<T = any> = {
size: number;
align?: number;
get(dataview: DataView, byteOffset: number): T;
set(dataview: DataView, byteOffset: number, value: T): void;
};
export type Descriptors<T = Descriptor<any>> = {
[key: string]: T;
};
export type DecodedBuffer<E extends Descriptors> = {
[K in keyof E]: ReturnType<E[K]["get"]>;
};
export function structSize(descriptors: Descriptors): number;
export function structAlign(descriptors: Descriptors): number;
export function ArrayOfBufferBackedObjects<T extends Descriptors>(buffer: ArrayBuffer, descriptors: T, { byteOffset, length, align }?: {
byteOffset?: number;
length?: number;
align?: number;
}): Array<DecodedBuffer<T>>;
export function BufferBackedObject<T extends Descriptors>(buffer: ArrayBuffer, descriptors: T, { byteOffset, align }?: {
byteOffset?: number;
align?: number;
}): DecodedBuffer<T>;
export interface EndiannessOption {
endianness: "little" | "big";
}
export interface AlignOption {
align: number;
}
export function Uint16({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<number>;
export function Uint32({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<number>;
export function Int16({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<number>;
export function Int32({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<number>;
export function Float32({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<number>;
export function Float64({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<number>;
export function BigInt64({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<bigint>;
export function BigUint64({ endianness, align, }?: Partial<EndiannessOption & AlignOption>): Descriptor<bigint>;
export function Uint8(): Descriptor<number>;
export function Int8(): Descriptor<number>;
export function NestedBufferBackedObject<T extends Descriptors>(descriptors: T): Descriptor<DecodedBuffer<T>>;
export function NestedArrayOfBufferBackedObjects<T extends Descriptors>(length: number, descriptors: T): Descriptor<Array<DecodedBuffer<T>>>;
export function UTF8String(maxBytes: number): Descriptor<string>;
export function reserved(size: number): Descriptor<void>;
}
type EndianOption = {
endianness?: "big" | "little";
};
export const BufferBackedObject: {
new <T extends Descriptors>(
buffer: ArrayBuffer,
descriptor: T,
opts?: BufferBackedObjectOptions
): DecodedBuffer<T>;
reserved(numBytes: number): Descriptor<number>;
Int8(): Descriptor<number>;
Uint8(): Descriptor<number>;
Int16(options?: EndianOption): Descriptor<number>;
Uint16(options?: EndianOption): Descriptor<number>;
Int32(options?: EndianOption): Descriptor<number>;
Uint32(options?: EndianOption): Descriptor<number>;
BigInt64(options?: EndianOption): Descriptor<number>;
BigUint64(options?: EndianOption): Descriptor<number>;
Float32(options?: EndianOption): Descriptor<number>;
Float64(options?: EndianOption): Descriptor<number>;
UTF8String(maxBytes: number): Descriptor<string>;
ArrayBuffer(size: number): Descriptor<ArrayBuffer>;
NestedBufferBackedObject<E extends Descriptors>(
descriptors: E
): Descriptor<DecodedBuffer<E>>;
NestedArrayOfBufferBackedObjects<A extends Descriptors>(
numItems: number,
descriptors: A
): Descriptor<Array<DecodedBuffer<A>>>;
};
export interface ArrayOfBufferBackedObjectsOptions {
byteOffset?: number;
length?: number;
}
export const ArrayOfBufferBackedObjects: {
new <T extends Descriptors>(
buffer: ArrayBuffer,
descriptors: T,
options?: ArrayOfBufferBackedObjectsOptions
): Array<DecodedBuffer<T>>;
};
export default BufferBackedObject;

@@ -1,2 +0,1 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).bufferBackedObject={})}(this,function(e){function t(e){return"symbol"==typeof e||isNaN(e)}function n(e){for(var t=0,n=0,r=Object.values(e);n<r.length;n++)t+=r[n].size;return t}function r(e,n,r){var i=void 0===r?{}:r,f=i.byteOffset,o=void 0===f?0:f,u=i.length,c=void 0===u?0:u,s=new DataView(e,o),a=0;n=Object.assign({},n);for(var b=0,d=Object.entries(n);b<d.length;b++){var g=d[b],l=g[1];n[g[0]]=Object.assign({},l,{offset:a}),a+=l.size}return c||(c=Math.floor((e.byteLength-o)/a)),new Proxy(new Array(c),{has:function(e,n){return t(n)?"buffer"===n||n in e:n<c},get:function(r,i,f){if("buffer"===i)return e;if(t(i)){var o=r[i];return"function"==typeof o&&(o=o.bind(f)),o}var u=parseInt(i),c=u*a;if(!(u>=r.length)){if(!r[u]){r[u]={};for(var b=function(){var e=g[d],t=e[1];if(!("get"in t))return"continue";Object.defineProperty(r[u],e[0],{enumerable:!0,get:function(){return t.get(s,c+t.offset)},set:function(e){return t.set(s,c+t.offset,e)}})},d=0,g=Object.entries(n);d<g.length;d++)b();Object.freeze(r[u])}return r[u]}}})}function i(e,t,n){var i=(void 0===n?{}:n).byteOffset;return r(e,t,{byteOffset:void 0===i?0:i})[0]}"object"!=typeof globalThis&&(Object.prototype.__defineGetter__("__magic__",function(){return this}),__magic__.globalThis=__magic__,delete Object.prototype.__magic__),["Uint16","Uint32","Int16","Int32","Float32","Float64","BigInt64","BigUint64"].forEach(function(e){i[e]=function(t){var n=(void 0===t?{}:t).endianness,r=void 0===n?"little":n;if("big"!==r&&"little"!==r)throw Error("Endianness needs to be either 'big' or 'little'");var i="little"===r;return{size:globalThis[e+"Array"].BYTES_PER_ELEMENT,get:function(t,n){return t["get"+e](n,i)},set:function(t,n,r){return t["set"+e](n,r,i)}}}}),i.Uint8=function(){return{size:1,get:function(e,t){return e.getUint8(t)},set:function(e,t,n){return e.setUint8(t,n)}}},i.Int8=function(){return{size:1,get:function(e,t){return e.getInt8(t)},set:function(e,t,n){return e.setInt8(t,n)}}},i.NestedBufferBackedObject=function(e){return{size:n(e),get:function(t,n){return new r(t.buffer,e,{byteOffset:n,length:1})[0]},set:function(e,t,n){throw Error("Can’t set an entire struct")}}},i.NestedArrayOfBufferBackedObjects=function(e,t){return{size:n(t)*e,get:function(n,i){return new r(n.buffer,t,{byteOffset:i,length:e})},set:function(e,t,n){throw Error("Can’t set an entire array")}}},i.UTF8String=function(e){return{size:e,get:function(t,n){return(new TextDecoder).decode(new Uint8Array(t.buffer,n,e)).replace(/\u0000+$/,"")},set:function(t,n,r){var i=(new TextEncoder).encode(r),f=new Uint8Array(t.buffer,n,e);f.fill(0),f.set(i.subarray(0,e))}}},i.reserved=function(e){return{size:e}},e.ArrayOfBufferBackedObjects=r,e.BufferBackedObject=i,e.default=i,e.structSize=n});
//# sourceMappingURL=buffer-backed-object.umd.js.map
(function(l,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(l=typeof globalThis<"u"?globalThis:l||self,s(l["buffer-backed-object"]={}))})(this,function(l){"use strict";function s(t){return typeof t=="symbol"?!1:!isNaN(t)}function b(t,e){let n=t-t%e;return t%e!=0&&(n+=e),n}function y(t){let e=0;for(const{align:n=1,size:i}of Object.values(t))e=b(e,n)+i;return e=b(e,O(t)),e}function O(t){return Math.max(...Object.values(t).map(e=>e.align??1))}function d(t,e,{byteOffset:n=0,length:i=0,align:r=O(e)}={}){const f=new DataView(t,n);let E=0;const B={...e};for(const[u,o]of Object.entries(B))B[u]={...o,offset:b(E,o.align??1)},E=B[u].offset+o.size;return E=b(E,r),i||(i=Math.floor((t.byteLength-n)/E)),new Proxy(new Array(i),{has(u,o){return s(o)?o<i:o==="buffer"?!0:o in u},get(u,o,N){if(o==="buffer")return t;if(!s(o)){let g=u[o];return typeof g=="function"&&(g=g.bind(N)),g}const c=parseInt(o),h=c*E;if(!(c>=u.length)){if(!u[c]){u[c]={};for(const[g,a]of Object.entries(B))"get"in a&&Object.defineProperty(u[c],g,{enumerable:!0,get(){return a.get(f,h+a.offset)},set(R){return a.set(f,h+a.offset,R)}});Object.freeze(u[c])}return u[c]}}})}function T(t,e,{byteOffset:n=0,align:i=1}={}){return d(t,e,{byteOffset:n,align:i})[0]}function U({endianness:t="little",align:e=2}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:Uint16Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getUint16(r,n),set:(i,r,f)=>i.setUint16(r,f,n)}}function w({endianness:t="little",align:e=4}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:Uint32Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getUint32(r,n),set:(i,r,f)=>i.setUint32(r,f,n)}}function I({endianness:t="little",align:e=2}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:Int16Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getInt16(r,n),set:(i,r,f)=>i.setInt16(r,f,n)}}function j({endianness:t="little",align:e=4}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:Int32Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getInt32(r,n),set:(i,r,f)=>i.setInt32(r,f,n)}}function z({endianness:t="little",align:e=4}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:Float32Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getFloat32(r,n),set:(i,r,f)=>i.setFloat32(r,f,n)}}function A({endianness:t="little",align:e=8}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:Float64Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getFloat64(r,n),set:(i,r,f)=>i.setFloat64(r,f,n)}}function _({endianness:t="little",align:e=8}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:BigInt64Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getBigInt64(r,n),set:(i,r,f)=>i.setBigInt64(r,f,n)}}function S({endianness:t="little",align:e=8}={}){if(t!=="big"&&t!=="little")throw Error("Endianness needs to be either 'big' or 'little'");const n=t==="little";return{align:e,size:BigUint64Array.BYTES_PER_ELEMENT,get:(i,r)=>i.getBigUint64(r,n),set:(i,r,f)=>i.setBigUint64(r,f,n)}}function F(){return{align:1,size:1,get:(t,e)=>t.getUint8(e),set:(t,e,n)=>t.setUint8(e,n)}}function M(){return{align:1,size:1,get:(t,e)=>t.getInt8(e),set:(t,e,n)=>t.setInt8(e,n)}}function P(t){const e=y(t);return{align:Object.values(t)[0].align??1,size:e,get:(n,i)=>d(n.buffer,t,{byteOffset:i,length:1})[0],set:(n,i,r)=>{throw Error("Can’t set an entire struct")}}}function v(t,e){const n=y(e)*t;return{align:Object.values(e)[0].align??1,size:n,get:(i,r)=>d(i.buffer,e,{byteOffset:r+i.byteOffset,length:t}),set:(i,r,f)=>{throw Error("Can’t set an entire array")}}}function k(t){return{align:1,size:t,get:(e,n)=>new TextDecoder().decode(new Uint8Array(e.buffer,n,t)).replace(/\u0000+$/,""),set:(e,n,i)=>{const r=new TextEncoder().encode(i),f=new Uint8Array(e.buffer,n,t);f.fill(0),f.set(r.subarray(0,t))}}}function L(t){return{align:1,size:t,get(){},set(){}}}l.ArrayOfBufferBackedObjects=d,l.BigInt64=_,l.BigUint64=S,l.BufferBackedObject=T,l.Float32=z,l.Float64=A,l.Int16=I,l.Int32=j,l.Int8=M,l.NestedArrayOfBufferBackedObjects=v,l.NestedBufferBackedObject=P,l.UTF8String=k,l.Uint16=U,l.Uint32=w,l.Uint8=F,l.reserved=L,l.structAlign=O,l.structSize=y,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
{
"name": "buffer-backed-object",
"version": "0.3.1",
"version": "1.0.0",
"description": "",
"repository": "github:GoogleChromeLabs/buffer-backed-object",
"homepage": "https://github.com/GoogleChromeLabs/buffer-backed-object#readme",
"source": "buffer-backed-object.js",
"module": "dist/buffer-backed-object.modern.js",
"source": "buffer-backed-object.ts",
"module": "dist/buffer-backed-object.js",
"main": "dist/buffer-backed-object.umd.js",
"types": "dist/buffer-backed-object.d.ts",
"scripts": {
"build": "mkdir -p dist && run-p build:*",
"build:bundle": "microbundle -f modern && microbundle -f umd",
"build:types": "cp buffer-backed-object.d.ts dist/",
"build": "mkdir -p dist && run-s build:bundle build:types",
"build:bundle": "vite build",
"build:types": "tsc --emitDeclarationOnly -d --outFile dist/buffer-backed-object.d.ts buffer-backed-object.ts",
"test": "run-p test:*",
"test:unit": "karmatic",
"test:unit": "vitest run --coverage",
"test:types": "tsc --noEmit buffer-backed-object.type-test.ts"

@@ -22,19 +22,9 @@ },

"devDependencies": {
"conditional-type-checks": "^1.0.5",
"husky": "^4.2.3",
"karmatic": "^1.4.0",
"lint-staged": "^10.1.2",
"microbundle": "^0.12.0-next.8",
"@vitest/coverage-c8": "^0.26.3",
"conditional-type-checks": "^1.0.6",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.4",
"webpack": "^4.42.1"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,css,md}": "prettier --write"
"typescript": "^4.9.4",
"vite": "^4.0.4",
"vitest": "^0.26.3"
}
}

@@ -19,21 +19,25 @@ # `BufferBackedObject`

### WebGPU
Similary, you can define structs in WGLS and read from/write to GPU memory buffers. With `BufferBackedObject` or `ArrayOfBufferBackedObjects`, you can manipulate those structs from JavaScript more easily and efficiently.
## Example
```js
import { BufferBackedObject } from "buffer-backed-object";
import * as BBO from "buffer-backed-object";
const buffer = new ArrayBuffer(100);
const view = new BufferBackedObject(buffer, {
id: BufferBackedObject.Uint16({ endianness: "big" }),
position: BufferBackedObject.NestedBufferBackedObject({
x: BufferBackedObject.Float32(),
y: BufferBackedObject.Float32(),
z: BufferBackedObject.Float32(),
const view = BBO.BufferBackedObject(buffer, {
id: BBO.BufferBackedObject.Uint16({ endianness: "big" }),
position: BBO.NestedBufferBackedObject({
x: BBO.Float32(),
y: BBO.Float32(),
z: BBO.Float32(),
}),
normal: BufferBackedObject.NestedBufferBackedObject({
x: BufferBackedObject.Float32(),
y: BufferBackedObject.Float32(),
z: BufferBackedObject.Float32(),
normal: BBO.NestedBufferBackedObject({
x: BBO.Float32(),
y: BBO.Float32(),
z: BBO.Float32(),
}),
textureId: BufferBackedObject.Uint8(),
textureId: BBO.Uint8(),
});

@@ -51,21 +55,18 @@

```js
import {
ArrayOfBufferBackedObjects,
BufferBackedObject,
} from "buffer-backed-object";
import * as BBO from "buffer-backed-object";
const buffer = new ArrayBuffer(100);
const view = new ArrayOfBufferBackedObjects(buffer, {
id: BufferBackedObject.Uint16({ endianness: "big" }),
position: BufferBackedObject.NestedBufferBackedObject({
x: BufferBackedObject.Float32(),
y: BufferBackedObject.Float32(),
z: BufferBackedObject.Float32(),
const view = BBO.ArrayOfBufferBackedObjects(buffer, {
id: BBO.Uint16({ endianness: "big" }),
position: BBO.NestedBufferBackedObject({
x: BBO.Float32(),
y: BBO.Float32(),
z: BBO.Float32(),
}),
normal: BufferBackedObject.NestedBufferBackedObject({
x: BufferBackedObject.Float32(),
y: BufferBackedObject.Float32(),
z: BufferBackedObject.Float32(),
normal: BBO.NestedBufferBackedObject({
x: BBO.Float32(),
y: BBO.Float32(),
z: BBO.Float32(),
}),
textureId: BufferBackedObject.Uint8(),
textureId: BBO.Uint8(),
});

@@ -91,32 +92,33 @@

### `new BufferBackedObject(buffer, descriptors, {byteOffset = 0})`
### `function BufferBackedObject(buffer, descriptors, {byteOffset = 0})`
The key/value pairs in the `descriptors` object must be declared in the same order as they are laid out in the buffer. The returned object has getters and setters for each of `descriptors` properties and de/serializes them `buffer`, starting at the given `byteOffset`.
The following descriptor types are available as built-ins:
### `function ArrayOfBufferBackedObjects(buffer, descriptors, {byteOffset = 0, length = 0})`
- `BufferBackedObject.reserved(numBytes)`: A number of unused bytes. This field will now show up in the object.
- `BufferBackedObject.Int8()`: An 8-bit signed integer
- `BufferBackedObject.Uint8()`: An 8-bit unsigned integer
- `BufferBackedObject.Int16({endianness = 'little'})`: An 16-bit signed integer
- `BufferBackedObject.Uint16({endianness = 'little'})`: An 16-bit unsigned integer
- `BufferBackedObject.Int32({endianness = 'little'})`: An 32-bit signed integer
- `BufferBackedObject.Uint32({endianness = 'little'})`: An 32-bit unsigned integer
- `BufferBackedObject.BigInt64({endianness = 'little'})`: An 64-bit signed [`BigInt`][bigint]
- `BufferBackedObject.BigUint64({endianness = 'little'})`: An 64-bit unsigned [`BigInt`][bigint]
- `BufferBackedObject.Float32({endianness = 'little'})`: An 32-bit IEEE754 float
- `BufferBackedObject.Float64({endianness = 'little'})`: An 64-bit IEEE754 float (“double”)
- `BufferBackedObject.UTF8String(maxBytes)`: A UTF-8 encoded string with the given maximum number of bytes. Trailing NULL bytes will be trimmed after decoding.
- `BufferBackedObject.ArrayBuffer(size)`: An `ArrayBuffer` of the given size
- `BufferBackedObject.NestedBufferBackedObject(descriptors)`: A nested `BufferBackedObject` with the given descriptors
- `BufferBackedObject.NestedArrayOfBufferBackedObjects(numItems, descriptors)`: A nested `ArrayOfBufferBackedObjects` of length `numItems` with the given descriptors
### `new ArrayOfBufferBackedObjects(buffer, descriptors, {byteOffset = 0, length = 0})`
Like `BufferBackedObject`, but returns an _array_ of `BufferBackedObject`s. If `length` is 0, as much of the buffer is used as possible. The array is populated lazily under the hood for performance purposes. That is, the individual `BufferBackedObject`s will only be created when their index is accessed.
### `structSize(descriptors)`
### `function structSize(descriptors)`
Returns the number of bytes required to store a value with the schema outlined by `descriptors`.
### Descriptors
The following descriptor types are available as individually exported functions
- `function reserved(numBytes)`: A number of unused bytes. This field will now show up in the object.
- `function Int8()`: An 8-bit signed integer
- `function Uint8()`: An 8-bit unsigned integer
- `function Int16({align = 2, endianness = 'little'})`: An 16-bit signed integer
- `function Uint16({align = 2, endianness = 'little'})`: An 16-bit unsigned integer
- `function Int32({align = 4, endianness = 'little'})`: An 32-bit signed integer
- `function Uint32({align = 4, endianness = 'little'})`: An 32-bit unsigned integer
- `function BigInt64({align = 8, endianness = 'little'})`: An 64-bit signed [`BigInt`][bigint]
- `function BigUint64({align = 8, endianness = 'little'})`: An 64-bit unsigned [`BigInt`][bigint]
- `function Float32({align = 4, endianness = 'little'})`: An 32-bit IEEE754 float
- `function Float64({align = 8, endianness = 'little'})`: An 64-bit IEEE754 float (“double”)
- `function UTF8String(maxBytes)`: A UTF-8 encoded string with the given maximum number of bytes. Trailing NULL bytes will be trimmed after decoding.
- `function NestedBufferBackedObject(descriptors)`: A nested `BufferBackedObject` with the given descriptors
- `function NestedArrayOfBufferBackedObjects(numItems, descriptors)`: A nested `ArrayOfBufferBackedObjects` of length `numItems` with the given descriptors
## Defining your own descriptor types

@@ -128,2 +130,3 @@

{
align?: 1, // Required aligment
size: 4, // Size required by the type

@@ -130,0 +133,0 @@ get(dataView, byteOffset) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc