Skip to content

Commit

Permalink
Serialization made compatible with zenoh-ext serialization API (#114)
Browse files Browse the repository at this point in the history
* serialization implementation

* add @thi.ng/leb128 dependency

* fix ci errors

* deserialization clean up

* update docs;
add generic serialization constraint;

* move zenoh ext to a submodule

* typo fix

* docs update

* format

* make Object ZPartialDeserializer require a new() constructor, rather than an object instance

* add support for specifying number or bigint serialization format

* add support for Uint8Array serialization

* add support for typed arrays
  • Loading branch information
DenisBiryukov91 authored Feb 10, 2025
1 parent a62a3be commit b98bc97
Show file tree
Hide file tree
Showing 16 changed files with 2,275 additions and 207 deletions.
8 changes: 4 additions & 4 deletions zenoh-ts/examples/chat/src/chat_session.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Config, Session, Queryable, Query, Liveliness, LivelinessToken, Reply, Sample, Receiver, KeyExpr, Subscriber, SampleKind, Publisher, deserialize_string } from '@eclipse-zenoh/zenoh-ts';
import { Config, Session, Queryable, Query, Liveliness, LivelinessToken, Reply, Sample, Receiver, KeyExpr, Subscriber, SampleKind, Publisher } from '@eclipse-zenoh/zenoh-ts';

export function validate_username(username: string): boolean {
return /^[a-zA-Z0-9_-]+$/.test(username);
Expand Down Expand Up @@ -96,8 +96,8 @@ export class ChatSession {
if (reply instanceof Reply) {
let resp = reply.result();
if (resp instanceof Sample) {
let payload = deserialize_string(resp.payload().to_bytes());
let attachment = deserialize_string(resp.attachment()?.to_bytes() ?? Uint8Array.from([]));
let payload = resp.payload().to_string();
let attachment = resp.attachment()?.to_string() ?? "";
log(`[Session] GetSuccess from ${resp.keyexpr().toString()}, messages: ${payload}, from user: ${attachment}`);
this.messages = JSON.parse(payload);
}
Expand All @@ -123,7 +123,7 @@ export class ChatSession {
this.message_subscriber = await this.session.declare_subscriber(KEYEXPR_CHAT_USER.join("*"),
{
handler: (sample: Sample) => {
let message = deserialize_string(sample.payload().to_bytes());
let message = sample.payload().to_string();
log(`[Subscriber] Received message: ${message} from ${sample.keyexpr().toString()}`);
let user = ChatUser.fromKeyexpr(sample.keyexpr());
if (user) {
Expand Down
143 changes: 142 additions & 1 deletion zenoh-ts/examples/chat/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==

"@eclipse-zenoh/zenoh-ts@file:../..":
version "1.0.3"
version "1.1.1"
dependencies:
"@thi.ng/leb128" "^3.1.36"
base64-arraybuffer "^1.0.2"
channel-ts "^0.1.2"
eslint "^9.10.0"
Expand Down Expand Up @@ -146,6 +147,141 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"

"@thi.ng/api@^8.11.19":
version "8.11.19"
resolved "https://registry.yarnpkg.com/@thi.ng/api/-/api-8.11.19.tgz#9ce8eb5657a28ddbd82a1864792b59a753a4ee33"
integrity sha512-ffK8nyNDd3kiwiijpb0Uv/0MtRUYpRtJj3K8OwFXdCzboll+/DNvXpfAlBMwSkLqZR0GighDwApyl0/4njiHCg==

"@thi.ng/arrays@^2.10.14":
version "2.10.14"
resolved "https://registry.yarnpkg.com/@thi.ng/arrays/-/arrays-2.10.14.tgz#a6202d3e208e2930554083b24c8a7143e23e8d56"
integrity sha512-hTuot92azDcX/GbWIFtbrKEPTThQn9tlRqKAnGL0xerCXEvHlvJ9F2zgf/crhBt8vcP8ca/P+4xYJYLKa54iBw==
dependencies:
"@thi.ng/api" "^8.11.19"
"@thi.ng/checks" "^3.6.22"
"@thi.ng/compare" "^2.4.11"
"@thi.ng/equiv" "^2.1.75"
"@thi.ng/errors" "^2.5.25"
"@thi.ng/random" "^4.1.10"

"@thi.ng/binary@^3.4.42":
version "3.4.42"
resolved "https://registry.yarnpkg.com/@thi.ng/binary/-/binary-3.4.42.tgz#2fdd71c7c06d9bfb38bd94b71598343ccf8d89a9"
integrity sha512-DU5JiBOONjx4synwos2GPaq8ByIfPSx6H6BsBmTf/Ou1PiABMlFuHlz8aOzw/Z1jUTYzb04arwUpYl1MVKjrAg==
dependencies:
"@thi.ng/api" "^8.11.19"

"@thi.ng/checks@^3.6.22":
version "3.6.22"
resolved "https://registry.yarnpkg.com/@thi.ng/checks/-/checks-3.6.22.tgz#00b3c6e4f8562f4108795b645a97cfc588c7cdcd"
integrity sha512-CMxQHrxvHnAVnmhxfP44GVS1TmAUWLsX8uZTVrL4IQcWzVVyHgvD6uXYw5E7kaZCR2S6wnS+Ng5oOCBE0EU/Hg==
dependencies:
tslib "^2.8.1"

"@thi.ng/compare@^2.4.11":
version "2.4.11"
resolved "https://registry.yarnpkg.com/@thi.ng/compare/-/compare-2.4.11.tgz#63cf4bebcd15575265f68bcf85d9b61c953c9cf6"
integrity sha512-SNbRrf19ntPmpXVkdMK04ZRRSDzPmRxxV9WKdH6+3J5NkYbASffi9o9WWun9In09P1+/Zh5yYfi2LzNsIDHNDw==
dependencies:
"@thi.ng/api" "^8.11.19"

"@thi.ng/compose@^3.0.22":
version "3.0.22"
resolved "https://registry.yarnpkg.com/@thi.ng/compose/-/compose-3.0.22.tgz#a7b4f4927fd896c0e37a797c8f2a2aa1f7a14271"
integrity sha512-PdXT/X4Ca8VfzyGryhAlw7PYyPvbRhpFqT4hej2oOgGuXeifoNr2J6WejTEwK8wA4F57onHHr7TisPVNfN/PMQ==
dependencies:
"@thi.ng/api" "^8.11.19"
"@thi.ng/errors" "^2.5.25"

"@thi.ng/equiv@^2.1.75":
version "2.1.75"
resolved "https://registry.yarnpkg.com/@thi.ng/equiv/-/equiv-2.1.75.tgz#f6fa49df08c1fd17da1302e057f1809cea3b5cb2"
integrity sha512-AoQQgrhW0xtXgxkrDK5EP5Y9Fe/QBGCmQgVKJ2G7uYCW/IbN/nTZEUM6011v41ydpmKl4wJjmr+s3StcIeDfdQ==

"@thi.ng/errors@^2.5.25":
version "2.5.25"
resolved "https://registry.yarnpkg.com/@thi.ng/errors/-/errors-2.5.25.tgz#4a9b91fbe51413b151d3758bdf6ef7e53cbc2d1f"
integrity sha512-PmK56hWGvRWr9Eq0V5xkV4tQvhjPtfvv6urFONP/D0gwFQXj+v6rcDYkAFLzOkbLqa0DYtkgvUsMklF/L53upA==

"@thi.ng/hex@^2.3.63":
version "2.3.63"
resolved "https://registry.yarnpkg.com/@thi.ng/hex/-/hex-2.3.63.tgz#75f91f6e4d4dec22046a74526b30f81d32d138f4"
integrity sha512-5xaQEKdaPls8de4zx4piPjCP13AHZLlyJCwHp4enSfeH5ZmDR3/aRwbmJod4FlJAvQdvDa3jBz+OtHNdxwrPLg==

"@thi.ng/leb128@^3.1.36":
version "3.1.36"
resolved "https://registry.yarnpkg.com/@thi.ng/leb128/-/leb128-3.1.36.tgz#9516fe5113d030a617039da3c8f5361d62185940"
integrity sha512-NfEfBpjtOwGJxgxDXyuLYxfmikT8u7TPT3J1rc51JmScxtTXZQkpgR3ORwy/xshILmGVMID2vww3NshZbSHqZg==
dependencies:
"@thi.ng/checks" "^3.6.22"
"@thi.ng/errors" "^2.5.25"
"@thi.ng/transducers-binary" "^2.1.152"

"@thi.ng/math@^5.11.19":
version "5.11.19"
resolved "https://registry.yarnpkg.com/@thi.ng/math/-/math-5.11.19.tgz#0a0ff7ad249f2960fcc3b39f930568d4a19a2b35"
integrity sha512-nsS1hP0rtgfuLtD6fLEtNTv07DlPZjKlgRoTrJcpkr47zzGcabwxo7a2VlHYFjhnShVUVSdQTFCnSSHN40drOQ==
dependencies:
"@thi.ng/api" "^8.11.19"

"@thi.ng/memoize@^4.0.9":
version "4.0.9"
resolved "https://registry.yarnpkg.com/@thi.ng/memoize/-/memoize-4.0.9.tgz#57ab73ed0e7ff98df94c7182a703b0dbd6edf8c3"
integrity sha512-YQD5lOew7mAZRripl3ylyOOq0OJ7yv/9D2o9w0Rd3ZrL2nl49tbticcH0V6nSKHEJhJSeRftCjmxNXNZulZkBw==
dependencies:
"@thi.ng/api" "^8.11.19"

"@thi.ng/random@^4.1.10":
version "4.1.10"
resolved "https://registry.yarnpkg.com/@thi.ng/random/-/random-4.1.10.tgz#732bae76ffb397ca42e771bbe94f6c8c2fbd815a"
integrity sha512-BBEAr0fg0pDoHtk4iBsdyLfZ9HcGvzeOwdAiXb3iibPhCL+h4lpjpmxy8yCyD7CatlnUICfbDdiY59X5qKJMgA==
dependencies:
"@thi.ng/api" "^8.11.19"
"@thi.ng/errors" "^2.5.25"

"@thi.ng/strings@^3.9.4":
version "3.9.4"
resolved "https://registry.yarnpkg.com/@thi.ng/strings/-/strings-3.9.4.tgz#782bbf81ea441c0d2671ca54654cf4c639762879"
integrity sha512-Ut9Elrp5HHiQjtJALjqKljzhDZYEgVMgwjN3EuEgHwBcrtvgthuhK6gKf2cGr0O8sqIjcctnhWqu7nE4QLuVaQ==
dependencies:
"@thi.ng/api" "^8.11.19"
"@thi.ng/errors" "^2.5.25"
"@thi.ng/hex" "^2.3.63"
"@thi.ng/memoize" "^4.0.9"

"@thi.ng/timestamp@^1.1.4":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@thi.ng/timestamp/-/timestamp-1.1.4.tgz#6800c1fd8f9fc349e3d601a445707f52f1fc7d97"
integrity sha512-Rsfa1ypD3Ulkh2RDbj19hb8hCXRzUjy+1gpbovy2Bnys7VPNXRyHEaEZvqRNzj+jTzylvFNbi8QLArX7RXzeqw==

"@thi.ng/transducers-binary@^2.1.152":
version "2.1.152"
resolved "https://registry.yarnpkg.com/@thi.ng/transducers-binary/-/transducers-binary-2.1.152.tgz#320c4875f73ce31e9bcca5b2481ef5e4365636a8"
integrity sha512-0QG5cTheIZGcIaDDsaCaSSYvDXhFqbmdFng8HNLzl5EIcSecRC0R3rT7N0SxN+JxGMDRmZFQk/vFHlqulxaTew==
dependencies:
"@thi.ng/binary" "^3.4.42"
"@thi.ng/compose" "^3.0.22"
"@thi.ng/errors" "^2.5.25"
"@thi.ng/hex" "^2.3.63"
"@thi.ng/random" "^4.1.10"
"@thi.ng/strings" "^3.9.4"
"@thi.ng/transducers" "^9.2.17"

"@thi.ng/transducers@^9.2.17":
version "9.2.17"
resolved "https://registry.yarnpkg.com/@thi.ng/transducers/-/transducers-9.2.17.tgz#8504f45178c3875841c9afcb9dcd68a3966c4b22"
integrity sha512-LjbAbRAcpHi0gv6aW7+5ijJYJjhQtJoszEvjCC025RQ3pjXXD3gkRs8nnJULk6ibkcEcCoP5Q0WBWNAXusInQg==
dependencies:
"@thi.ng/api" "^8.11.19"
"@thi.ng/arrays" "^2.10.14"
"@thi.ng/checks" "^3.6.22"
"@thi.ng/compare" "^2.4.11"
"@thi.ng/compose" "^3.0.22"
"@thi.ng/errors" "^2.5.25"
"@thi.ng/math" "^5.11.19"
"@thi.ng/random" "^4.1.10"
"@thi.ng/timestamp" "^1.1.4"

"@types/eslint-scope@^3.7.7":
version "3.7.7"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
Expand Down Expand Up @@ -1568,6 +1704,11 @@ ts-loader@^9.2.6:
semver "^7.3.4"
source-map "^0.7.4"

tslib@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==

tslog@^4.9.3:
version "4.9.3"
resolved "https://registry.yarnpkg.com/tslog/-/tslog-4.9.3.tgz#d4167d5f51748bdeab593945bc2d8f9827ea0dba"
Expand Down
121 changes: 121 additions & 0 deletions zenoh-ts/examples/deno/src/z_bytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//
// Copyright (c) 2025 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//

import { ZBytes } from "@eclipse-zenoh/zenoh-ts";
import { ZBytesSerializer, ZBytesDeserializer, ZSerializeable, ZDeserializeable, zserialize, zdeserialize, ZS, ZD, NumberFormat, BigIntFormat } from "@eclipse-zenoh/zenoh-ts/ext";


class MyStruct implements ZSerializeable, ZDeserializeable {
v1: number;
v2: string;
v3: number[];

constructor(v1?: number, v2?: string, v3?: number[]) {
if (typeof v1 !== `undefined`) {
this.v1 = v1;
} else {
this.v1 = 0;
}
if (typeof v2 !== `undefined`) {
this.v2 = v2;
} else {
this.v2 = ""
}
if (typeof v3 !== `undefined`) {
this.v3 = v3;
} else {
this.v3 = new Array<number>()
}
}

serialize_with_zserializer(serializer: ZBytesSerializer): void {
serializer.serialize(this.v1, ZS.number(NumberFormat.Int32))
serializer.serialize(this.v2)
serializer.serialize(this.v3, ZS.array(ZS.number(NumberFormat.Int8)))
}

deserialize_with_zdeserializer(deserializer: ZBytesDeserializer): void {
this.v1 = deserializer.deserialize(ZD.number(NumberFormat.Uint32))
this.v2 = deserializer.deserialize(ZD.string())
this.v3 = deserializer.deserialize(ZD.array(ZD.number(NumberFormat.Int8)))
}

to_string(): string {
return JSON.stringify(this)
}

}

export async function main() {
// using raw data
// string
{
let input = "test"
let payload = new ZBytes(input)
let output = payload.to_string()
console.log(`Input: ${input}, Output: ${output}`)
}
// Uint8Array
{
let input = new Uint8Array([1, 2, 3, 4])
let payload = new ZBytes(input)
let output = payload.to_bytes()
console.log(`Input: ${input}, Output: ${output}`)
}

// serialization
// array
{
let input = [1, 2, 3, 4]
// by default number is serialized/deserialized as 64-bit float,
// other formats, like Int32, for example, must be specified explicitly
let payload = zserialize(input, ZS.array(ZS.number(NumberFormat.Int32)))
let output = zdeserialize(ZD.array(ZD.number(NumberFormat.Int32)), payload)
// let payload = zserialize(input)
// let output = zdeserialize(ZD.array(ZD.number()), payload)
console.log(`Input: ${input}, Output: ${output}`)
}
// typed array
{
let input = new Int32Array([1, 2, 3, 4])
let payload = zserialize(input)
let output = zdeserialize(ZD.int32array(), payload)
console.log(`Input: ${input}, Output: ${output}`)
}
// map
{
let input = new Map<bigint, string>()
input.set(0n, "abc")
input.set(1n, "def")
// by default bigint is serialized/deserialized as 64-bit signed integer,
// other formats, like UInt64, for example, must be specified explicitly
let payload = zserialize(input, ZS.map(ZS.bigint(BigIntFormat.Uint64), ZS.string()))
let output = zdeserialize(ZD.map(ZD.bigint(BigIntFormat.Uint64), ZD.string()), payload)
// let payload = zserialize(input)
// let output = zdeserialize(ZD.map(ZD.bigint(), ZD.string()), payload)
console.log(`Input:`)
console.table(input)
console.log(`Output:`)
console.table(output)
}
// Custom class
{
let input = new MyStruct(1234, "test", [1, 2, 3, 4])
let payload = zserialize(input)
let output = zdeserialize(ZD.object(MyStruct), payload)
console.log(`Input: ${input.to_string()}, Output: ${output.to_string()}`)
}
}

main();
10 changes: 5 additions & 5 deletions zenoh-ts/examples/deno/src/z_get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// ZettaScale Zenoh Team, <[email protected]>
//

import { deserialize_string, ReplyError, Config, Receiver, RecvErr, Sample, Session, QueryTarget } from "@eclipse-zenoh/zenoh-ts";
import { ReplyError, Config, Receiver, RecvErr, Sample, Session, QueryTarget } from "@eclipse-zenoh/zenoh-ts";
import { Duration, Milliseconds } from 'typed-duration'
const { milliseconds } = Duration
import { BaseParseArgs } from "./parse_args.ts";
Expand All @@ -30,10 +30,10 @@ export async function main() {
// let resp = reply.result();
// if (resp instanceof Sample) {
// let sample: Sample = resp;
// console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().deserialize(deserialize_string), "')");
// console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().to_string()), "')");
// } else {
// let reply_error: ReplyError = resp;
// console.warn(">> Received (ERROR: '", reply_error.payload().deserialize(deserialize_string), "')");
// console.warn(">> Received (ERROR: '", reply_error.payload().to_string(), "')");
// }
// };

Expand All @@ -57,10 +57,10 @@ export async function main() {
const resp = reply.result();
if (resp instanceof Sample) {
const sample: Sample = resp;
console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().deserialize(deserialize_string), "')");
console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().to_string(), "')");
} else {
const reply_error: ReplyError = resp;
console.warn(">> Received (ERROR: '{", reply_error.payload().deserialize(deserialize_string), "}')");
console.warn(">> Received (ERROR: '{", reply_error.payload().to_string(), "}')");
}
}
reply = await receiver.receive();
Expand Down
4 changes: 2 additions & 2 deletions zenoh-ts/examples/deno/src/z_get_liveliness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//

import {
deserialize_string, Sample, Config, Session, KeyExpr,
Sample, Config, Session, KeyExpr,
Receiver,
RecvErr,
ReplyError
Expand Down Expand Up @@ -45,7 +45,7 @@ export async function main() {
console.warn(">> Alive token ('", sample.keyexpr(), ")");
} else {
const reply_error: ReplyError = resp;
console.warn(">> Received (ERROR: '", reply_error.payload().deserialize(deserialize_string), "')");
console.warn(">> Received (ERROR: '", reply_error.payload().to_string(), "')");
}
}
reply = await receiver.receive();
Expand Down
2 changes: 1 addition & 1 deletion zenoh-ts/examples/deno/src/z_ping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function main() {
console.warn("Warming up for 5 seconds...");

const startTime = new Date();
const data = [122, 101, 110, 111, 104];
const data = new Uint8Array([122, 101, 110, 111, 104]);

while (elapsed(startTime) < 5) {
pub.put(data);
Expand Down
6 changes: 3 additions & 3 deletions zenoh-ts/examples/deno/src/z_querier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// ZettaScale Zenoh Team, <[email protected]>
//

import { deserialize_string, ReplyError, Config, Receiver, RecvErr, Sample, Session, QueryTarget, Selector, } from "@eclipse-zenoh/zenoh-ts";
import { ReplyError, Config, Receiver, RecvErr, Sample, Session, QueryTarget, Selector, } from "@eclipse-zenoh/zenoh-ts";
import { Duration, Milliseconds } from 'typed-duration'
import { BaseParseArgs } from "./parse_args.ts";

Expand Down Expand Up @@ -41,10 +41,10 @@ export async function main() {
const resp = reply.result();
if (resp instanceof Sample) {
const sample: Sample = resp;
console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().deserialize(deserialize_string), "')");
console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().to_string(), "')");
} else {
const reply_error: ReplyError = resp;
console.warn(">> Received (ERROR: '{", reply_error.payload().deserialize(deserialize_string), "}')");
console.warn(">> Received (ERROR: '{", reply_error.payload().to_string(), "}')");
}
}
reply = await receiver.receive();
Expand Down
Loading

0 comments on commit b98bc97

Please sign in to comment.