Security News
PyPI Introduces Digital Attestations to Strengthen Python Package Security
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
A set of tools to encode any data structure to buffer and consume without deserialization
A set of tools to encode any data structure to buffer and consume without deserialization
LazyArray
is one of the invention that derived from our work on the custom data format.LazyArray
which allows us to lazily combines values from multiple data column helps address this challenge.Optional
and OneOf
.name: { field1: Type, field2: Type }
for objects with fields (named tuple)name: [ Type, Type, Type ]
for multiple values in tuple formname: Type
for aliasing another typename: Modifier<Type>
Array<Type>
Map<Type>
Optional<Type>
OneOf<Type, Type>
Ref<Type>
- for self-referencesLink<Type>
- for reference to data in another containerOptional<Array<Type>>
Array<Optional<Type>>
- the two are not the sameUint8
Int8
Uint16
Int16
Uint32
Int32
Float32
Float64
String
Vector2
Vector3
Vector4
Matrix3
Matrix4
// schema.js
import { extendSchema } from 'buffer-ql';
export const SCHEMA = extendSchema(
{
// define custom primitives here
},
{
'#': {
trackedEntities: 'Array<TrackedEntity>',
trackedEntitiesOfInterest: 'Map<TrackedEntityRef>',
},
TrackedEntity: {
id: 'Int32',
class: 'Uint8',
pose: 'Pose',
velocity: 'Optional<Vector3>',
source: 'TrackedEntitySource',
waypoints: 'Optional<Array<TrackedEntityWayPoint>>'
},
...
// rest of type definitions
}
);
// read.js
import { createReader, ALL_VALUES } from 'buffer-ql';
import SCHEMA from './schema.js';
const Reader = createReader(buffer, SCHEMA);
const reader = new Reader('#', 1);
.get(key)
const trackedEntitiesReader = reader.get('trackedEntities').get(ALL_VALUES);
.value()
const trackedEntities = trackedEntitiesReader.get('class').value()
.get(key)
always return another reader.get(key)
behaves like ?.[key]
in JavaScript object accessReader
can be traversed until a dead end is reached from the schema PoV not the actual data PoV. In another word, user do not need to call .value()
every time to check if undefined
has been reached. User can just traverse all the way to the target spot and call .value()
at the endALL_VALUES
and ALL_KEYS
can be use to extract all elements from an array or map (column based reading).get(key)
on a reader that is pointing at an array or a map, an index array .get([index1, index2, index3])
or string array .get([key1, key2, key3])
can be provided respectively instead of the wildcard symbol ALL_VALUES
OneOf
type, a special BranchedReader
instance is returned. Further traversal defaults to assuming user is on the first branch. To continue traversing the alternate branch(es), use the .switchBranch(branchIndex)
method. Eg.const branchedReader = reader.get('someFieldWithOneOfType');
const oneOfValue = reader
.get('field1') // traversing on first branch [0]
.get('field11')
.switchBranch()
.get('field2') // traversing on second branch [1]
.get('field22')
.value()
.value()
returns either a single value or a LazyArray
holding values (to be elaborated in a further section) depending on whether reader is in single value mode or multi values (column reading) mode..singleValue()
to check which mode reader is in..value()
on a primitive type will return a primitive value (or an LazyArray that holds primitive value).value()
on a compound type will return the whole object by recursively walking the type treeLazyArray
will by returned in place of the array object to allow lazy decoding. Eg.// returns a LazyArray holding tracked entity objects
const trackedEntities = reader
.get('trackedEntities')
.get(ALL_VALUES)
.value();
const firstValue = trackedEntities.get(0);
const allValues = [...trackedEntities];
.value()
to return a LazyArray
of LazyArray
holding values. eg.// returns LazyArray<LazyArray<TrackedEntityWayPoint> | undefined>
const waypoints = reader
.get('trackedEntities')
.get(ALL_VALUES)
.get('waypoints')
.value();
const unpacked = [...waypoints.map(pts => [...pts])];
LazyArray
are accessed lazilyimport { LazyArray } from 'buffer-ql';
// by providing a read-only array
const arr1 = new LazyArray(source);
// by providing a getter function and a length value
const arr2 = new LazyArray(i => customSource.get(i), customSource.length);
const lazyArray = new LazyArray(source);
// by indexing
const firstValue = lazyArray.get(0);
const lastValue = lazyArray.get(arr.length - 1);
// by iterating
for (const v of lazyArray) {
// do something with v
}
lazyArray.forEach(v => {
// do something with v
});
const allValues = [...lazyArray];
// if you know the data type
const allValuesAsFloat32 = lazyArray.copyTo(Float32Array);
.forEach
.map
Re-indexing functions
.reverse
.slice
.filter
.sort
Reducing functions
.reduce
.reduceRight
.every
.some
.find
.findIndex
.indexOf
.lastIndexOf
.includes
getter
function is passed into the LazyArray
constructor, no call of getter
is being made until a value is accessed. Eg.const spy = jest.spyOn(customSource, 'get');
const getter = i => customSource.get(i);
const lazyArray = new LazyArray(getter, customSource.length);
lazyArray.slice();
lazyArray.reverse();
expect(spy).not.toBeCalled(); // `.slice` & `.reverse` do not access value
lazyArr.findIndex(v => true);
expect(spy).toHaveBeenCalledTimes(1); // returns after first value is accessed
.map
returns another LazyArray
. Lazily evaluated also. Eg.const mocked = jest.fn(v => 2 * v);
lazyArray.map(mocked);
expect(mocked).not.toBe.Called();
.map
calls, user may prefer eager evaluation to avoid duplicating expensive computation. .eagerEvaluate
method is provided for this purpose. Eg.const mappedA = lazyArr.map(someExpensiveComputation);
const mappedB = mappedA.map(fromAtoB);
const mappedC = mappedC.map(fromAtoC);
.eagerEvaluate
const optimizedMappedA = lazyArr.map(someExpensiveComputation).eagerEvaluate();
.eagerEvaluate()
returns a LazyArray
but the values inside optimizedMapped
are pre-computed and stored in a normal JavaScript Array
another approach for eager evaluation is calling the method .copyTo
and supplying an array constructor
LazyArray
also has a .findAll
method for re-indexing based on a secondary array
const reindexed = lazyArray.findAll(
secondary,
(elementOfSecondary, elementOfLazyArray) => a.id === b.id
);
const allValuesReader = reader.get(ALL_VALUES);
const columnA = allValuesReader.get('colA').value();
const columnB = allValuesReader.get('colB').value();
const mapped = new LazyArray({ a: columnA, b: columnB }, columnA.length)
.map(d => d.a + d.b);
const allValuesReader = reader.get(ALL_VALUES);
const columnA = allValuesReader.get('colA').value();
const columnB = allValuesReader.get('colB').value();
const columnX = allValuesReader.get('colC').get('colX').value();
const columnY = allValuesReader.get('colC').get('colY').value();
const combined = {
a: columnA,
b: columnB,
c: {
x: columnX,
y: columnY
}
};
const mapped = new LazyArray(combined, columnA.length)
.map(d => d.a + d.b + d.c.x + d.c.y);
import { createEncoder } from 'buffer-ql';
import { SCHEMA } from './schema.js';
const encoded = createEncoder(SCHEMA)(DATA, '#');
// supply name of root type ('#') in third argument
export const SCHEMA = extendSchema(
{
SourceTypeEnum: {
size: 1,
decode: (dv, offset) => ['Lidar', 'Camera'][dv.getUint8(offset)],
encode: (dv, offset, value) => {
dv.setUint8(offset, ['Lidar', 'Camera'].indexOf(value));
}
}
},
{
TrackedEntities: {
id: 'Int32',
class: 'Uint8',
pose: 'Pose'
source: 'SourceTypeEnum'
},
...
// rest of type definitions
},
);
transform
function can be provided to transform all instances of a certain type to the shape that is required. Using the third argument of extendSchema
. Eg.export const SCHEMA = extendSchema(
{
// define custom primitives here
},
{
// type definitions
},
{
TrackedEntities: d => {
return {
...d,
waypoints: d.waypoints || d.deprecatedWaypointName
}
}
}
);
OneOf
types should be accomplanied with a check
function to discriminate from other branch(es). Not required if branch is a provided base primitive. Eg.export const SCHEMA = extendSchema(
{
// define custom primitives here
},
{
...
CoordinatesOrPlaceName: 'OneOf<Vector2,PlaceName>',
PlaceName: {
long: 'String',
short: 'String'
}
},
{},
{
PlaceName: d => typeof d.long === 'string'
// check for `Vector2` not required since it is one of the provided base primitives
}
);
const filteredTrackedEntities = trackedEntitiesReader
.get(ALL_VALUES)
.value()
.filter(d => d.class === 3);
.value()
upfront hence unpacking the entire data object even though we only need to read from the class
attribute. We provide an API to do this in a more performant way.const filteredTrackedEntities = trackedEntitiesReader
.get(ALL_VALUES)
.apply.filter(v => v === 3)
.on(reader => reader.get('class'))
.value();
.value()
until filtering is done.apply.filter().on()
.apply.sort().on()
.apply.findAll().on()
.apply.dropNull().on()
.apply.reverse()
.apply.slice()
.apply.duplicate()
.apply
should be followed by one or more .forEach
const filteredWaypoints = trackedEntitiesReader
.get(ALL_VALUES)
.get('waypoints')
.apply.dropNull()
.on()
.get(ALL_VALUES)
.apply.forEach.dropNull()
.on(reader => reader.get('probability'))
.apply.forEach.filter(v => v > 0.5)
.on(reader => reader.get('probability'))
const waypointsReader = trackedEntitiesReader
.get(ALL_VALUES)
.get('waypoints')
.apply.dropNull()
.on()
.get(ALL_VALUES)
.get('pose')
.get('position');
const dumped = waypointsReader.dump(Float32);
FAQs
A set of tools to encode any data structure to buffer and consume without deserialization
We found that buffer-ql demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.