Skip to content

Commit 90752e4

Browse files
committed
binaryXX data type support added
1 parent 00b8256 commit 90752e4

File tree

10 files changed

+120
-15
lines changed

10 files changed

+120
-15
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## v1.1.0
2+
* Added support for binary type (uses Uint8List). Format: binary<LENGTH>, for example: binary12.
3+
* Color schemes used to print messages are updated: list items are now displayed using color of corresponding data type.
4+
15
## v1.0.0
26
* Enums and Types can be referenced from another manifest files.
37
* Enum declaration implemented.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ You can declare fields of standard type (such as uint32, double, string), custom
119119
- float - 32 bits (from -3.4E+38 to 3.4E+38, ~7 significant digits)
120120
- double - 64 bits (from -1.7E+308 to 1.7E+308, ~16 significant digits)
121121

122+
### Binary
123+
```json
124+
"mongo_id": "binary12",
125+
"some_hash": "binary64"
126+
```
127+
Declaration above defines two binary buffers: 12 and 64 bytes length.
128+
122129
### Bool
123130
```json
124131
"parameter": "bool"
@@ -186,6 +193,7 @@ In this case additional classes will be created: "SendUpdateMessageValues" and "
186193
## Arrays
187194
If you need to declare a field as an array of specific type, just put your type string into square brackets:
188195
```json
196+
"object_ids": ["binary12"],
189197
"numbers": ["uint32"],
190198
"names": ["string"],
191199
"users": ["@user"]

example/example.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010
},
1111
"test": [
1212
{
13+
"req_id": "binary12",
14+
"req_ids": ["binary4"],
1315
"req_int8": "int8",
1416
"req_uint16": "uint16",
1517
"req_double": "double",
1618
"req_bool": "bool",
1719
"req_string": "string",
20+
"?opt_id": "binary12",
21+
"?opt_ids": ["binary4"],
1822
"?opt_int8": "int8",
1923
"?opt_uint16": "uint16",
2024
"?opt_double": "double",

example/generated/example.generated.mjs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {PackMe, PackMeMessage} from 'packme';
1+
import {PackMe, PackMeMessage} from '../../lib/packme.mjs';
22

33
/** @enum {number} */
44
const TypeEnum = {
@@ -51,11 +51,15 @@ class NestedObject extends PackMeMessage {
5151
}
5252

5353
class TestMessage extends PackMeMessage {
54+
/** @type {!Uint8Array} */ reqId;
55+
/** @type {!Uint8Array[]} */ reqIds;
5456
/** @type {!number} */ reqInt8;
5557
/** @type {!number} */ reqUint16;
5658
/** @type {!number} */ reqDouble;
5759
/** @type {!boolean} */ reqBool;
5860
/** @type {!string} */ reqString;
61+
/** @type {?Uint8Array} */ optId;
62+
/** @type {?Uint8Array[]} */ optIds;
5963
/** @type {?number} */ optInt8;
6064
/** @type {?number} */ optUint16;
6165
/** @type {?number} */ optDouble;
@@ -69,11 +73,15 @@ class TestMessage extends PackMeMessage {
6973
/** @type {?NestedObject} */ optNested;
7074

7175
constructor(
76+
/** !Uint8Array */ reqId,
77+
/** !Uint8Array[] */ reqIds,
7278
/** !number */ reqInt8,
7379
/** !number */ reqUint16,
7480
/** !number */ reqDouble,
7581
/** !boolean */ reqBool,
7682
/** !string */ reqString,
83+
/** ?Uint8Array */ optId,
84+
/** ?Uint8Array[] */ optIds,
7785
/** ?number */ optInt8,
7886
/** ?number */ optUint16,
7987
/** ?number */ optDouble,
@@ -88,6 +96,8 @@ class TestMessage extends PackMeMessage {
8896
) {
8997
super();
9098
if (arguments.length > 0) {
99+
this.$check('reqId', reqId);
100+
this.$check('reqIds', reqIds);
91101
this.$check('reqInt8', reqInt8);
92102
this.$check('reqUint16', reqUint16);
93103
this.$check('reqDouble', reqDouble);
@@ -97,11 +107,15 @@ class TestMessage extends PackMeMessage {
97107
this.$check('reqEnum', reqEnum);
98108
this.$check('reqNested', reqNested);
99109
}
110+
this.reqId = reqId;
111+
this.reqIds = reqIds;
100112
this.reqInt8 = reqInt8;
101113
this.reqUint16 = reqUint16;
102114
this.reqDouble = reqDouble;
103115
this.reqBool = reqBool;
104116
this.reqString = reqString;
117+
this.optId = optId;
118+
this.optIds = optIds;
105119
this.optInt8 = optInt8;
106120
this.optUint16 = optUint16;
107121
this.optDouble = optDouble;
@@ -118,8 +132,19 @@ class TestMessage extends PackMeMessage {
118132
/** @return {number} */
119133
$estimate() {
120134
this.$reset();
121-
let bytes = 22;
135+
let bytes = 35;
136+
bytes += 4;
137+
bytes += 4 * this.reqIds.length;
122138
bytes += this.$stringBytes(this.reqString);
139+
this.$setFlag(this.optId != null);
140+
if (this.optId != null) {
141+
bytes += 12;
142+
}
143+
this.$setFlag(this.optIds != null);
144+
if (this.optIds != null) {
145+
bytes += 4;
146+
bytes += 4 * this.optIds.length;
147+
}
123148
this.$setFlag(this.optInt8 != null);
124149
if (this.optInt8 != null) {
125150
bytes += 1;
@@ -162,12 +187,20 @@ class TestMessage extends PackMeMessage {
162187
/** @return {undefined} */
163188
$pack() {
164189
this.$initPack(475203406);
165-
for (let i = 0; i < 1; i++) this.$packUint8(this.$flags[i]);
190+
for (let i = 0; i < 2; i++) this.$packUint8(this.$flags[i]);
191+
this.$packBinary(this.reqId, 12);
192+
this.$packUint32(this.reqIds.length);
193+
for (let item of this.reqIds) this.$packBinary(item, 4);
166194
this.$packInt8(this.reqInt8);
167195
this.$packUint16(this.reqUint16);
168196
this.$packDouble(this.reqDouble);
169197
this.$packBool(this.reqBool);
170198
this.$packString(this.reqString);
199+
if (this.optId != null) this.$packBinary(this.optId, 12);
200+
if (this.optIds != null) {
201+
this.$packUint32(this.optIds.length);
202+
for (let item of this.optIds) this.$packBinary(item, 4);
203+
}
171204
if (this.optInt8 != null) this.$packInt8(this.optInt8);
172205
if (this.optUint16 != null) this.$packUint16(this.optUint16);
173206
if (this.optDouble != null) this.$packDouble(this.optDouble);
@@ -188,12 +221,28 @@ class TestMessage extends PackMeMessage {
188221
/** @return {undefined} */
189222
$unpack() {
190223
this.$initUnpack();
191-
for (let i = 0; i < 1; i++) this.$flags.push(this.$unpackUint8());
224+
for (let i = 0; i < 2; i++) this.$flags.push(this.$unpackUint8());
225+
this.reqId = this.$unpackBinary(12);
226+
this.reqIds = [];
227+
let reqIdsLength = this.$unpackUint32();
228+
for (let i = 0; i < reqIdsLength; i++) {
229+
this.reqIds.push(this.$unpackBinary(4));
230+
}
192231
this.reqInt8 = this.$unpackInt8();
193232
this.reqUint16 = this.$unpackUint16();
194233
this.reqDouble = this.$unpackDouble();
195234
this.reqBool = this.$unpackBool();
196235
this.reqString = this.$unpackString();
236+
if (this.$getFlag()) {
237+
this.optId = this.$unpackBinary(12);
238+
}
239+
if (this.$getFlag()) {
240+
this.optIds = [];
241+
let optIdsLength = this.$unpackUint32();
242+
for (let i = 0; i < optIdsLength; i++) {
243+
this.optIds.push(this.$unpackBinary(4));
244+
}
245+
}
197246
if (this.$getFlag()) {
198247
this.optInt8 = this.$unpackInt8();
199248
}
@@ -233,7 +282,7 @@ class TestMessage extends PackMeMessage {
233282

234283
/** @return {string} */
235284
toString() {
236-
return `TestMessage\x1b[0m(reqInt8: ${PackMe.dye(this.reqInt8)}, reqUint16: ${PackMe.dye(this.reqUint16)}, reqDouble: ${PackMe.dye(this.reqDouble)}, reqBool: ${PackMe.dye(this.reqBool)}, reqString: ${PackMe.dye(this.reqString)}, optInt8: ${PackMe.dye(this.optInt8)}, optUint16: ${PackMe.dye(this.optUint16)}, optDouble: ${PackMe.dye(this.optDouble)}, optBool: ${PackMe.dye(this.optBool)}, optString: ${PackMe.dye(this.optString)}, reqList: ${PackMe.dye(this.reqList)}, optList: ${PackMe.dye(this.optList)}, reqEnum: ${PackMe.dye(this.reqEnum)}, optEnum: ${PackMe.dye(this.optEnum)}, reqNested: ${PackMe.dye(this.reqNested)}, optNested: ${PackMe.dye(this.optNested)})`;
285+
return `TestMessage\x1b[0m(reqId: ${PackMe.dye(this.reqId)}, reqIds: ${PackMe.dye(this.reqIds)}, reqInt8: ${PackMe.dye(this.reqInt8)}, reqUint16: ${PackMe.dye(this.reqUint16)}, reqDouble: ${PackMe.dye(this.reqDouble)}, reqBool: ${PackMe.dye(this.reqBool)}, reqString: ${PackMe.dye(this.reqString)}, optId: ${PackMe.dye(this.optId)}, optIds: ${PackMe.dye(this.optIds)}, optInt8: ${PackMe.dye(this.optInt8)}, optUint16: ${PackMe.dye(this.optUint16)}, optDouble: ${PackMe.dye(this.optDouble)}, optBool: ${PackMe.dye(this.optBool)}, optString: ${PackMe.dye(this.optString)}, reqList: ${PackMe.dye(this.reqList)}, optList: ${PackMe.dye(this.optList)}, reqEnum: ${PackMe.dye(this.reqEnum)}, optEnum: ${PackMe.dye(this.optEnum)}, reqNested: ${PackMe.dye(this.reqNested)}, optNested: ${PackMe.dye(this.optNested)})`;
237286
}
238287
}
239288

example/test.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ console.log('ok');
55
let pm = new PackMe(console.error);
66
pm.register(exampleMessageFactory);
77
let msg = new TestMessage(
8+
new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12]),
9+
[new Uint8Array([10, 20, 30, 40]), new Uint8Array([210, 220, 230, 240])],
810
23,
911
1,
1012
3.3,
1113
true,
1214
'something new',
15+
new Uint8Array([201,202,203,204,205,206,207,208,209,210,211,212]),
16+
[new Uint8Array([4, 3, 2, 1]), new Uint8Array([40, 30, 20, 10]), new Uint8Array([255, 255, 255, 255])],
1317
24,
1418
2,
1519
4.4,
@@ -24,5 +28,6 @@ let msg = new TestMessage(
2428
);
2529

2630
let data;
31+
console.log(msg.toString())
2732
console.log(data = pm.pack(msg));
28-
console.log(pm.unpack(data));
33+
console.log(pm.unpack(data).toString());

lib/compiler/field.mjs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,27 @@ class MessageField {
1010
type;
1111
optional;
1212
array;
13+
binary;
14+
binaryLength;
1315

1416
constructor(message, name, type, optional, array) {
1517
this.message = message;
1618
this.name = name;
1719
this.type = type;
1820
this.optional = optional;
1921
this.array = array;
22+
if (typeof type === 'string' && /^binary\d+$/.test(type)) {
23+
let length = parseInt(type.substring(6)) || 0;
24+
if (length <= 0) throw new Error(`Invalid binary data length for "${this.name}" in "${this.message.filename}".`);
25+
this.type = 'binary';
26+
this.binary = true;
27+
this.binaryLength = length;
28+
}
29+
else {
30+
this.type = type;
31+
this.binary = false;
32+
this.binaryLength = 0;
33+
}
2034
}
2135

2236
/// Returns required JS Type depending on field type.
@@ -41,7 +55,8 @@ class MessageField {
4155
case 'string':
4256
return 'string';
4357
default:
44-
if (this.type instanceof Enum) return this.type.name;
58+
if (this.binary) return 'Uint8Array';
59+
else if (this.type instanceof Enum) return this.type.name;
4560
else if (this.type instanceof Message) return this.type.name;
4661
else throw new Error(`Unknown data type "${this.type}" for "${this.name}" in "${this.message.filename}".`);
4762
}
@@ -64,15 +79,17 @@ class MessageField {
6479
case 'datetime': return `packDateTime(${name})`;
6580
case 'string': return `packString(${name})`;
6681
default:
67-
if (this.type instanceof Enum) return `packUint8(${name})`;
82+
if (this.binary) return `packBinary(${name}, ${this.binaryLength})`;
83+
else if (this.type instanceof Enum) return `packUint8(${name})`;
6884
else if (this.type instanceof Message) return `packMessage(${name})`;
6985
else throw new Error(`Unknown data type "${this.type}" for "${this.name}" in "${this.message.filename}".`);
7086
}
7187
}
7288

7389
/// Returns required unpack method depending on field type.
7490
get #unpack() {
75-
if (this.type instanceof Enum) return `$unpackUint8()`;
91+
if (this.binary) return `$unpackBinary(${this.binaryLength})`;
92+
else if (this.type instanceof Enum) return `$unpackUint8()`;
7693
else if (this.type instanceof Message) return `$unpackMessage(new ${this.type.name}())`;
7794
else return `$un${this.#pack('')}`;
7895
}
@@ -98,14 +115,16 @@ class MessageField {
98115
...(this.optional ? [`this.$setFlag(this.${this.name} != null);`] : []),
99116
...(this.optional ? [`if (this.${this.name} != null) {`] : []),
100117
...(!this.array ? [
101-
(typeof this.type === 'string' || this.type instanceof Enum) && this.type !== 'string' ? `bytes += ${sizeOf(this.type)};`
118+
this.binary ? `bytes += ${this.binaryLength};`
119+
: (typeof this.type === 'string' || this.type instanceof Enum) && this.type !== 'string' ? `bytes += ${sizeOf(this.type)};`
102120
: this.type === 'string' ? `bytes += this.$stringBytes(this.${this.name});`
103121
: this.type instanceof Message ? `bytes += this.${this.name}.$estimate();`
104122
: (() => { throw new Error(`Wrong type "${this.type}" for field "${this.name}" in "${this.message.filename}".`); })()
105123
]
106124
: [
107125
'bytes += 4;',
108-
(typeof this.type === 'string' || this.type instanceof Enum) && this.type !== 'string' ? `bytes += ${sizeOf(this.type)} * this.${this.name}.length;`
126+
this.binary ? `bytes += ${this.binaryLength} * this.${this.name}.length;`
127+
: (typeof this.type === 'string' || this.type instanceof Enum) && this.type !== 'string' ? `bytes += ${sizeOf(this.type)} * this.${this.name}.length;`
109128
: this.type === 'string' ? `for (let i = 0; i < this.${this.name}.length; i++) bytes += this.$stringBytes(this.${this.name}[i]);`
110129
: this.type instanceof Message ? `for (let i = 0; i < this.${this.name}.length; i++) bytes += this.${this.name}[i].$estimate();`
111130
: (() => { throw new Error(`Wrong type "${this.type}" for field "${this.name}" in "${this.message.filename}".`); })()

lib/compiler/message.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class Message extends FieldType {
6565

6666
this.fields[fieldName] = new MessageField(this, fieldName, value, optional, array);
6767
if (!optional && !array && (typeof value === 'string' || value instanceof Enum) && value !== 'string') {
68-
if (sizeOf(value) != null) this.bufferSize += sizeOf(value);
68+
if (this.fields[fieldName].binary) this.bufferSize += this.fields[fieldName].binaryLength;
69+
else if (sizeOf(value) != null) this.bufferSize += sizeOf(value);
6970
else throw new Error(`Unknown type "${value}" for field "${fieldName}" of "${this.name}" in "${this.filename}".`);
7071
}
7172
}

lib/message.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ class PackMeMessage {
104104
return message;
105105
}
106106

107+
/** @return {undefined} */
108+
$packBinary(/** Uint8Array */ value, /** number */ length) {
109+
for (let i = 0; i < length; i++) {
110+
this.$view.setUint8(this.#offset++, value[i] || 0);
111+
}
112+
}
107113
/** @return {undefined} */
108114
$packBool(/** boolean */ value) {
109115
this.$view.setUint8(this.#offset, value ? 1 : 0);
@@ -175,6 +181,12 @@ class PackMeMessage {
175181
}
176182
}
177183

184+
/** @return {Uint8Array} */
185+
$unpackBinary(/** number */ length) {
186+
let value = new Uint8Array(length);
187+
for (let i = 0; i < length; i++) value[i] = this.$view.getUint8(this.#offset++);
188+
return value;
189+
}
178190
/** @return {boolean} */
179191
$unpackBool() {
180192
let value = this.$view.getUint8(this.#offset);

lib/packme.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class PackMe {
1212
static dye(/** any */ x) {
1313
return typeof x === 'string' ? `\x1b[32m${x}\x1b[0m`
1414
: typeof x === 'number' || typeof x === 'bigint' || typeof x === 'boolean' ? `\x1b[34m${x}\x1b[0m`
15+
: x instanceof Uint8Array ? `\x1b[36m[${x}]\x1b[0m`
16+
: x instanceof Array ? `[${x.map((item) => this.dye(item)).join(', ')}]`
1517
: `\x1b[35m${x}\x1b[0m`;
1618
}
1719

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
22
"name": "packme",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Simple and extremely fast binary serialization using classes generated from JSON manifest files.",
55
"main": "lib/packme.mjs",
66
"module": "lib/packme.mjs",
7-
"private": true,
87
"scripts": {
98
"build": "rollup -c",
10-
"dev": "rollup -c -w"
9+
"dev": "rollup -c -w",
10+
"example": "node example/example.mjs",
11+
"compile": "node lib/compiler.mjs example example/generated"
1112
},
1213
"keywords": [],
1314
"author": "",

0 commit comments

Comments
 (0)