1.3.2 • Published 6 months ago

@xobj/core v1.3.2

Weekly downloads
-
License
GPL-3.0-only
Repository
github
Last release
6 months ago

@xobj/core

Build npm Downloads Coverage

Decoding and encoding JavaScript / TypeScript objects to compact binary format.

Available basic types:

For all basic types used optimization for data minification.

Recursive objects links are also supported.

You can see more examples in tests.

Also you can use custom types and replacers.

Install

yarn add @xobj/core

Usage

Basic usage with default types

// import library methods
import { encode, decode } from '@xobj/core';

interface User {
	name: string,
	age: number,
	gender?: 'male' | 'female',
	children?: User[],
}

// some kind of object
const source: User = {
	name: 'John Doe',
	age: 33,
	gender: 'male',
	children: [
		{ name: 'Jane', age: 12, gender: 'male' },
		{ name: 'Jack', age: 6 },
	],
};

// encode object to binary data
const buffer: ArrayBuffer = encode(source);

// decode binary data to object
const target: User = decode(buffer);

// use object
console.log(target.name);// John Doe
console.log(target!.children![0].age);// 12

Custom types usage

import { encode, decode, EncodeContext, DecodeContext, ValueType } from '@xobj/core';

class Point {
	constructor(public x: number, public y: number) {
	}
}

enum CustomType { POINT = 0 }

const source = {
	color: 0xff00ff,
	points: [
		new Point(1, 2),
		new Point(3, 4),
		new Point(5, 6),
	],
};

// encode

function customDetect(value: any): ValueType {
	if (value instanceof Point) {
		return ValueType.CUSTOM;
	}
	return ValueType.UNKNOWN;
}

function customEncode(value: any, context: EncodeContext) {
	const { writer } = context;

	if (value instanceof Point) {
		writer.writeUint8(CustomType.POINT);
		writer.writeUint8(value.x);
		writer.writeUint8(value.y);
	} else {
		throw `Unknown custom type: ${value}`;
	}
}

const buffer = encode(source, { customDetect, customEncode });

// decode

function customDecode(context: DecodeContext): any {
	const { reader } = context;
	const type = reader.readUint8() as CustomType;
	switch (type) {
		case CustomType.POINT:
			return new Point(
				reader.readUint8(),
				reader.readUint8()
			);
		default:
			throw `Unknown custom type: ${type}`;
	}
}

const target = decode(buffer, { customDecode });

// use object
console.log(target.points[0].x) // 1
console.log(target.points[0].y) // 2

See more about BufferWriter and BufferReader

Encode options

encode(value, {
	bufferSize, // buffer starter size, by default is 1024 bytes
	customDetect, // function for custom type detection
	customEncode, // function for custom type encoding
	floatQuality, // encoding float quality : 'double' | 'single' | number (default is 'double')
	replacer, // replacer method or table: (value: any) => any | Map<any,any> | map entries
});

The floatQuality parameter allows you to select the encoding type for floating point numbers.

Encoding all float numbers into float64 format (8 bytes)

encode(value); // by default float quality is 'double'

Encoding all float numbers into float32 format (4 bytes)

encode(value, { floatQuality: 'single' });

Encoding all float numbers into intVar format (1-9+ bytes). In this case floatQuality is number (divider / multiplier). For decoding it is used as multiplier, and for decoding it is used as divider. For example:

const buffer = encode({ x: 123.456, y: -3456.789 }, { floatQuality: 100 });
// 'x' and 'y' will be transformed to 'integer' and encoded as 'intVar' 
// floor(123.456 * 100) => 12345 => write intVar to 2 bytes
// floor(-3456.789 * 100) => -345678 => write intVar to 3 bytes

const value = decode(buffer);
// 'x' and 'y' will be decoded as 'intVar' and transformed to 'float' 
// read intVar from 2 bytes => 12345 / 100 => 123.45
// read intVar from 3 bytes => -345678 / 100 => -3456.78

Decode options

decode(value, {
	customDecode, // function for custom type decoding
	replacer, // replacer method or table: (value: any) => any | Map<any,any> | map entries
});

You can see more examples in tests.

Check out integration samples.

Replacers

You can use 3 replacer types

Via function:

const id = Symbol('id');

const source = {
	x: 1,
	update(value: number) {
		this.x += value;
	},
	id,
};

const buffer = encode(source, {
	replacer: (value) => {
		if (value === id) return 'id-0';
		if (value === source.update) return 345678;
		return value;
	},
});

const target = decode(buffer, {
	replacer: (value) => {
		if (value === 'id-0') return id;
		if (value === 345678) return source.update;
		return value;
	},
});

Via Map table:

const id = Symbol('id');

const source = {
	x: 1,
	update(value: number) {
		this.x += value;
	},
	id,
};

const buffer = encode(source, {
	replacer: new Map<any, any>([
		[id, 'id-0'],
		[source.update, 345678],
	]),
});

const target = decode(buffer, {
	replacer: new Map<any, any>([
		['id-0', id],
		[345678, source.update],
	]),
});

Via map entries table:

const id = Symbol('id');

const source = {
	x: 1,
	update(value: number) {
		this.x += value;
	},
	id,
};

const buffer = encode(source, {
	replacer: [
		[id, 'id-0'],
		[source.update, 345678],
	],
});

const target = decode(buffer, {
	replacer: [
		['id-0', id],
		[345678, source.update],
	],
});

Samples

File format (xobj)

Coming soon

Development

Install all dependencies

yarn

Build project

yarn build

Test project

yarn test

Generate coverage report

yarn coverage

Check code quality

yarn lint
1.3.2

6 months ago

1.3.1

1 year ago

1.3.0

1 year ago

1.2.0

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago

0.5.0

2 years ago

0.4.8

2 years ago

0.4.7

2 years ago

0.4.6

2 years ago

0.4.5

2 years ago

0.4.4

2 years ago

0.4.3

2 years ago

0.4.2

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.3.0

2 years ago

0.2.3

2 years ago

0.2.2

2 years ago

0.2.1

2 years ago