@zacharygriffee/struct v0.1.21
z-struct
A struct-like implementation in vanilla Javascript
A struct keeps variables together in memory. Where a class keeps references together.
This library utilizes compact-encoding library to encode types to buffer.
Installation
npm install @zacharygriffee/struct --save
Import
import {Struct, TYPES, registerType} from "@zacharygriffee/struct";
Usage examples
Create Struct
// struct.js
// Use the design factory to access the variables by name
const Vector3 = Struct(
({int32}) => [
int32("x"),
int32("y"),
int32("z")
]
);
const numbers = [5,4,3];
const vec3 = Vector3(numbers);
const {x, y, z, buffer: vec3Buffer} = vec3; // destructurable
const [x, y, z] = vec3; // is iterable
vec3.subscribe(
buffer => {
// do something with updated buffer
}
);
vec3.x = 5; // Changing the variable updates the buffer.
vec3.next({x: 3, y: 2}); // partial changes.
numbers[0] === 3; // the array the struct encompasses also changes.
numbers[0] = 42; // changing the array also changes the struct and buffer and 'causes reaction'
Use the struct
const Vector2 = Struct(({float64}) => [float64("x"), float64("y")]);
const vec2 = Vector2([5.5, 0.3]); // Okay
const vec2 = Vector2({x: 5.5, y: 0.3}); // Okay
// Pull values from struct instance
const [x,y] = vec2; // okay
const {x,y} = vec2; // okay
// Get buffer of values
const {buffer} = vec2; // okay
const buffer = vec2.buffer; // obviously
// rxjs
vec2.subscribe(buffer => {
/* buffer updates when struct values change */
});
vec2.change$.subscribe(struct => {
/* updates when struct values change */
})/
// Partial applications.
vec2.next({x: 6.2, y: 0.5}); // okay
vec2.next({x: 2}); // okay
vec2.next([6.2]); // okay, must keep structure order
vec2.next([6.2, 0.5]); // okay, must keep structure order
vec2.copyFrom([6.2, 0.5]); // alias for next
vec2.next(buff); // okay
vec2.copyFrom(buff); // alias for next
// free it
vec2.dispose(); // relieve resources and reactivity
vec2.disposed === true; // Dispose leaves 'disposed' property for checking
// you must create a new struct after dispose()
API
Struct(designOrDesignFactory)
There are two ways to create a struct.
One | Design
This way creates an 'arrayLike' struct where you access by index number.
import {TYPES, Struct} from "@zacharygriffee/struct";
const {
int32, float64
} = TYPES;
const AgeWeight = Struct([Int32, Float64]); // Must pass it as an array.
const {0: age, 1: weight} = AgeWeight([25, 123.52]); // okay
const [age, weight] = AgeWeight([25, 123.52]); // okay
Two | Design Factory
Pass a function as first argument of struct and use destructuring to access the type names. The design factory must return an array.
import {Struct} from "@zacharygriffee/struct";
const vec2 = Struct(({float64}) =>
[
float64("x"),
float64("y")
]
);
// OR verbosely
Struct((TYPES) => {
return [
TYPES.float64("x"),
TYPES.float64("y")
]
}
);
struct.copyFrom(otherStructOrStructBuffer)
| struct.next(otherStructOrStructBuffer)
The following can be copied from
- Copy from another struct
- Copy from another buffer created by similar struct
- Copy from object that has all or partial properties of current struct
- Copy from an ordered array all or partial the struct design
struct.dispose()
All properties and functions of struct will become undefined except for a 'disposed' property which will be true. All reactions will cease to operate, and the buffer naturally gced unless referenced elsewhere. The target of the struct will be unattached.
IF your app is ephemeral in nature, like a webpage, you don't really need to dispose.
struct.subscribe(buffer => {})
Get the subscription of buffer changes. Google RxJS usage for more information.
The struct is a Subject
and fully compatible with RxJS library. So it can be piped, and shared,
and whatever operator combination you can dream of.
struct.change$.subscribe(struct => {})
Get the subscription of value changes. struct
will be the full struct of each change.
struct.toArray()
Returns the structure as an array with the order of the design. The resulting array does not cause reactions.
struct.toJSON()
Creates an array of the struct property, values and type name, suitable for non-buffer json serialization.
import {Struct} from "@zacharygriffee/struct";
const Vector2 = Struct(({float64}) =>
[
float64("x"),
float64("y")
]
);
const vec2 = Vector2([5.5, 0.12345]);
const obj = vec2.toJSON();
// newVec2 and vec2 are conceptually equal but are not connected.
const newVec2 = Vector2.fromJSON(obj);
Struct.fromJSON(structOrBuffer, [raw=false])
structOrBuffer
supports an array, stringified array, or buffer.
raw=false
set this to true if the buffer has no framing like leading length of json.
If the structure json was created by JSON.stringify
with Buffer.from
then raw should be set to true.
[Symbol.iterator]
Iterate the properties of the struct ordered to the design.
struct.encode
&& struct.decode
&& struct.preencode
See compact-encoding for more information.
The struct functions as a compact-encoding
except that it uses its own values to encode and decode into and out of the
buffer. This happens reactively anyway so typical usage wouldn't need to use these functions.
However, there are tools out there that take an encoder in their configuration where passing the struct would be more efficient and readable.
const Vector1 = Struct(({int32}) => [int32("x")]);
const vector1 = Vector1([0]);
vector1.change$.subscribe(vector1 => {
// receives changes.
});
const bee = new Bee({valueEncoding: vector1});
vector1 = await bee.get("someKey"); // updates and returns 'vector1'
I may create a mapper that provides middleware between the changes and the reactions. But you can easily do that with rxjs operators.
import {map, distinctUntilChanged} from "rxjs";
vector1.change$.pipe(
map(o => o.age),
distinctUntilChanged()
).subscribe(o => o === changedAge)
TYPES
See compact-encoding for supporting types with the exceptions of raw, fixed and array modifiers.
Alias types:
object
alias to json
number
alias to float64
Utilities
Struct.registerType(name, encoder)
- name: Give the type a name. (e.g.
int32
is a type name). The type name must be a valid javascript identifier and not conflict with already defined types. - encoder: See compact-encoding on how to create an encoder.
Test it
npm test
Roadmap
- Reference (read and write) the variables straight from the buffer instead of having a buffer change reactively.
- Implement the structured clone to pass the reference of buffer instead of copying
- Use a struct design that references a buffer in un-ordered ways. E.G. If I have x,y,z buffer I could create a design that references it like z,x
Distributed under the MIT license. See LICENSE
for more information.