├── .gitattributes
├── doc
├── webpkiorg.png
├── empty.svg
├── back.svg
├── closed.svg
├── open.svg
├── xtl.svg
├── release-notes.txt
├── style.css
├── cbor.js.svg
├── playground.html
└── non-finite-numbers.html
├── test
├── package.json
├── assertions.js
├── clone.js
├── dynamic.js
├── simple.js
├── cotx.js
├── test-nan-payload.js
├── utf8.js
├── out-of-range.js
├── hex.js
├── sequence.js
├── xyz-encoder.js
├── base64.js
├── nondeterministic.js
├── xyz-decoder.js
├── int-ranges.js
├── tags.js
├── miscellaneous.js
├── maps.js
├── float32.js
├── arrays.js
├── integer.js
├── diagnostic.js
├── node-crypto.mjs
├── check-for-unread.js
├── map-performance.js
├── number-table.mjs
├── dates.js
├── float.js
└── test-all.js
├── .gitignore
├── npm
├── package.json
├── README.md
└── test-all.mjs
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Disable LF normalization for all files
2 | * -text
--------------------------------------------------------------------------------
/doc/webpkiorg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyberphone/CBOR.js/main/doc/webpkiorg.png
--------------------------------------------------------------------------------
/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "devDependencies": {
4 | "eslint": "^8.55.0"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | cbor.js.sln
3 | *.tgz
4 | bin
5 | .tmp
6 | test/node_modules
7 | test/package-lock.json
8 | .DS_Store
9 |
10 |
--------------------------------------------------------------------------------
/doc/empty.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/doc/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/doc/closed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/doc/open.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test/assertions.js:
--------------------------------------------------------------------------------
1 | // Support for Node.js testing
2 |
3 | export function assertTrue(text, bool) {
4 | if (!bool) throw Error("Assertion: " + text);
5 | }
6 |
7 | export function assertFalse(text, bool) {
8 | if (bool) throw Error("Assertion: " + text);
9 | }
10 |
11 | export function fail(text) {
12 | throw Error("Fail: " + text);
13 | }
14 |
15 | export function success() {
16 | console.log("SUCCESSFUL");
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/test/clone.js:
--------------------------------------------------------------------------------
1 | // Testing the "clone()" and "equals() methods
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | let object = CBOR.Map()
6 | .set(CBOR.Int(2), CBOR.Array()
7 | .add(CBOR.Boolean(false)));
8 | assertTrue("clone+equals", object.equals(object.clone()));
9 | let copy = object.clone().set(CBOR.Int(1), CBOR.String("Hi"));
10 | assertFalse("copy+equals+clone", copy.equals(object));
11 |
12 | success();
13 |
--------------------------------------------------------------------------------
/doc/xtl.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/test/dynamic.js:
--------------------------------------------------------------------------------
1 | // dynamic tests
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, success } from './assertions.js';
4 |
5 | assertTrue("dyn", CBOR.Map().setDynamic(wr =>
6 | wr.set(CBOR.Int(1), CBOR.Boolean(true))).get(CBOR.Int(1)).getBoolean());
7 |
8 | let option = "on";
9 | assertTrue("dyn", CBOR.Map().setDynamic(wr => {
10 | if (option) {
11 | wr.set(CBOR.Int(1), CBOR.String(option));
12 | }
13 | return wr;
14 | }).get(CBOR.Int(1)).getString() == option);
15 |
16 | function lambda(wr) {
17 | wr.set(CBOR.Int(1), CBOR.Boolean(true));
18 | return wr;
19 | }
20 | assertTrue("dyn", CBOR.Map().setDynamic(lambda).get(CBOR.Int(1)).getBoolean());
21 |
22 | success();
23 |
--------------------------------------------------------------------------------
/test/simple.js:
--------------------------------------------------------------------------------
1 | // Testing "simple"
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | [-1, 256, 24, 31].forEach(value => {
6 | try {
7 | CBOR.Simple(value);
8 | throw Error("Should not");
9 | } catch (error) {
10 | if (!error.toString().includes("out of range")) {
11 | throw error;
12 | }
13 | }
14 | });
15 |
16 | function oneTurn(value, hex) {
17 | let s = CBOR.Simple(value);
18 | let s2 = CBOR.decode(s.encode());
19 | assertTrue("v", s.getSimple() == value);
20 | assertTrue("v2", s2.getSimple() == value);
21 | assertTrue("b", CBOR.toHex(s2.encode()) == hex);
22 | }
23 |
24 | oneTurn(0, "e0");
25 | oneTurn(23, "f7");
26 | oneTurn(32, "f820");
27 | oneTurn(255, "f8ff");
28 |
29 | success();
30 |
--------------------------------------------------------------------------------
/test/cotx.js:
--------------------------------------------------------------------------------
1 | // Testing the COTX identifier
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function oneTurn(hex, dn, ok) {
6 | try {
7 | let object = CBOR.decode(CBOR.fromHex(hex));
8 | assertTrue("Should not execute", ok);
9 | if (object.toString() != dn.toString() || !object.equals(CBOR.decode(object.encode()))) {
10 | throw Error("non match:" + dn + " " + object.toString());
11 | }
12 | } catch (error) {
13 | if (ok) console.log(error.toString());
14 | assertFalse("Must succeed", ok);
15 | }
16 | }
17 |
18 | oneTurn('d903f2623737', '1010("77")', false);
19 | oneTurn('d903f281623737', '1010(["77"])', false);
20 | oneTurn('d903f28206623737', '1010([6, "77"])', false);
21 | oneTurn('d903f28262373707', '1010(["77", 7])', true);
22 |
23 | success();
24 |
--------------------------------------------------------------------------------
/npm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cbor-object",
3 | "version": "1.0.16",
4 | "description": "CBOR: deterministic encoder/decoder, diagnostic notation encoder/decoder, and utilities",
5 | "homepage": "https://github.com/cyberphone/CBOR.js#cborjs",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/cyberphone/CBOR.js.git#main"
9 | },
10 | "keywords": [
11 | "CBOR",
12 | "RFC8949",
13 | "Deterministic, API"
14 | ],
15 | "exports": {
16 | ".": {
17 | "import": "./mjs/index.mjs",
18 | "require": "./cjs/index.cjs"
19 | }
20 | },
21 | "scripts": {
22 | "test": "node ./test-all.mjs"
23 | },
24 | "author": {
25 | "name": "Anders Rundgren",
26 | "email": "anders.rundgren.net@gmail.com"
27 | },
28 | "engines": {
29 | "node": ">=14.18.1"
30 | },
31 | "license": "MIT",
32 | "dependencies": {
33 | "npm": "*"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/test-nan-payload.js:
--------------------------------------------------------------------------------
1 | // Test program for verifying NaN with payload support
2 |
3 | function oneNanWithPayloadTurn(hex) {
4 | let bin = BigInt("0x" + hex);
5 |
6 | let buffer = new Uint8Array(8);
7 | for (let q = 8; --q >= 0;) {
8 | buffer[q] = Number(bin & 0xffn);
9 | bin >>= 8n;
10 | }
11 | let value = new DataView(buffer.buffer, 0, 8).getFloat64(0, false);
12 | let u8 = new Uint8Array(8);
13 | new DataView(u8.buffer, 0, 8).setFloat64(0, value, false);
14 |
15 | let ok = true;
16 | for (let q = 0; q < 8; q++) {
17 | if (buffer[q] != u8[q]) {
18 | ok = false;
19 | }
20 | }
21 | console.log("ok=" + ok + " hex=" + hex + " read=" + u8);
22 | }
23 |
24 | oneNanWithPayloadTurn("7ff8000000000000");
25 | oneNanWithPayloadTurn("7ff0000000000001");
26 | oneNanWithPayloadTurn("fff0000000000001");
27 | oneNanWithPayloadTurn("7fffffffffffffff");
28 | oneNanWithPayloadTurn("fff8000000000000");
--------------------------------------------------------------------------------
/test/utf8.js:
--------------------------------------------------------------------------------
1 | // Test of "utf8" converters
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function utf8EncoderTest(string, ok) {
6 | try {
7 | CBOR.String(string).encode();
8 | assertTrue("enc", ok);
9 | } catch (error) {
10 | assertFalse("No good", ok);
11 | }
12 |
13 | }
14 |
15 | function utf8DecoderTest(hex, ok) {
16 | let cbor = CBOR.fromHex(hex);
17 | let roundTrip;
18 | try {
19 | roundTrip = CBOR.decode(cbor).encode();
20 | } catch (error) {
21 | assertFalse("No good", ok);
22 | return;
23 | }
24 | assertTrue("OK", ok);
25 | assertFalse("Conv", CBOR.compareArrays(cbor, roundTrip));
26 | }
27 |
28 | utf8DecoderTest("62c328", false);
29 | utf8DecoderTest("64f0288cbc", false);
30 | utf8DecoderTest("64f0908cbc", true);
31 | utf8EncoderTest("Hi", true)
32 | utf8EncoderTest("\uD83D", false);
33 | utf8EncoderTest("\uD83D\uDE2D", true);
34 |
35 | success();
36 |
--------------------------------------------------------------------------------
/test/out-of-range.js:
--------------------------------------------------------------------------------
1 | // Number overflow tests.
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | const TOO_BIG = Number.MAX_SAFE_INTEGER + 1;
6 | const IN_RANGE = Number.MAX_SAFE_INTEGER;
7 |
8 | try {
9 | CBOR.Int(TOO_BIG);
10 | throw Error('Should not');
11 | } catch (error) {
12 | if (error.toString().includes('Should not')) {
13 | throw error;
14 | }
15 | }
16 | let cbor = CBOR.BigInt(BigInt(TOO_BIG)).encode();
17 | try {
18 | CBOR.decode(cbor).getInt53();
19 | throw Error('Should not');
20 | } catch (error) {
21 | if (error.toString().includes('Should not')) {
22 | throw error;
23 | }
24 | }
25 | assertTrue("big", BigInt(TOO_BIG) == CBOR.decode(cbor).getBigInt());
26 |
27 | cbor = CBOR.Int(IN_RANGE).encode();
28 | assertTrue("R0", CBOR.decode(cbor).getInt53() == IN_RANGE);
29 | cbor = CBOR.Int(-IN_RANGE).encode();
30 | assertTrue("R0", CBOR.decode(cbor).getInt53() == -IN_RANGE);
31 |
32 | success();
33 |
--------------------------------------------------------------------------------
/test/hex.js:
--------------------------------------------------------------------------------
1 | // Test of "hex" utility methods
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | const hex = '0123456789abcdefABCDEF';
6 |
7 | let bin = CBOR.fromHex(hex);
8 | let cnv = CBOR.toHex(bin);
9 | assertFalse("hex", CBOR.compareArrays(bin, CBOR.fromHex(cnv)));
10 | let ref = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef]);
11 | assertFalse("bin", CBOR.compareArrays(bin, ref));
12 | try {
13 | CBOR.fromHex("AAA");
14 | throw Error("should not");
15 | } catch (error) {
16 | if (!error.toString().includes("Unev")) {
17 | console.log(error);
18 | }
19 | }
20 |
21 | try {
22 | CBOR.fromHex("Ag");
23 | throw Error("should not");
24 | } catch (error) {
25 | if (!error.toString().includes("Bad hex")) {
26 | console.log(error);
27 | }
28 | }
29 | // Zero hex is accepted as well...
30 | assertFalse("zero", CBOR.compareArrays(CBOR.fromHex(''), new Uint8Array()));
31 | success();
32 |
--------------------------------------------------------------------------------
/npm/README.md:
--------------------------------------------------------------------------------
1 | ## cbor-object
2 | Core Description: https://github.com/cyberphone/CBOR.js#cborjs
3 |
4 | ### Nodejs Installation
5 | ```code
6 | npm install cbor-object
7 | ```
8 |
9 | If `cbor-object` is to be installed as a **global** package, perform the following:
10 | ```code
11 | npm install cbor-object -g
12 | cd "to local work directory"
13 | npm link cbor-object
14 | ```
15 | ### Usage in Node.js
16 | The above works for both CJS and MJS (ESM) applications. MJS example:
17 | ```cbor
18 | // test.mjs
19 | import CBOR from 'cbor-object';
20 |
21 | let cbor = CBOR.Map()
22 | .set(CBOR.Int(1), CBOR.Float(45.7))
23 | .set(CBOR.Int(2), CBOR.String("Hi there!")).encode();
24 |
25 | console.log(CBOR.toHex(cbor));
26 | ```
27 | ### Usage in Browsers
28 | Copy the file https://github.com/cyberphone/CBOR.js/blob/main/src/cbor.js to your application and include it in HTML with the `` tag.
29 |
30 | ### Release Notes
31 | See file https://cyberphone.github.io/CBOR.js/doc/release-notes.txt
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Anders Rundgren
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/sequence.js:
--------------------------------------------------------------------------------
1 | // Testing the "sequence" option
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | let cbor = new Uint8Array([0x05, 0xa1, 0x05, 0x42, 0x6a, 0x6a])
6 | try {
7 | CBOR.decode(cbor);
8 | throw Error("Should not");
9 | } catch (error) {
10 | if (!error.toString().includes('Unexpected')) console.log(error);
11 | }
12 | let decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
13 | let total = new Uint8Array();
14 | let object;
15 | while (object = decoder.decodeWithOptions()) {
16 | total = CBOR.addArrays(total, object.encode());
17 | }
18 | assertFalse("Comp", CBOR.compareArrays(total, cbor));
19 | assertTrue("Comp2", total.length == decoder.getByteCount());
20 | decoder = CBOR.initDecoder(new Uint8Array(), CBOR.SEQUENCE_MODE);
21 | assertFalse("Comp3", decoder.decodeWithOptions());
22 | assertTrue("Comp4", decoder.getByteCount() == 0);
23 | let arraySequence = CBOR.Array();
24 | decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
25 | while (object = decoder.decodeWithOptions()) {
26 | arraySequence.add(object);
27 | }
28 | assertFalse("Comp5", CBOR.compareArrays(arraySequence.encodeAsSequence(), cbor));
29 |
30 | success();
31 |
--------------------------------------------------------------------------------
/test/xyz-encoder.js:
--------------------------------------------------------------------------------
1 | // Simple "encoder" API
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | class XYZEncoder {
6 |
7 | static COUNTER = CBOR.Int(1);
8 | static TEMPERATURE = CBOR.Int(2);
9 | static GREETING = CBOR.Int(3);
10 |
11 | #map;
12 |
13 | constructor() {
14 | this.#map = CBOR.Map();
15 | }
16 |
17 | setCounter = function(intVal) {
18 | this.#map.set(XYZEncoder.COUNTER, CBOR.Int(intVal));
19 | return this;
20 | }
21 |
22 | setTemperature = function(floatVal) {
23 | this.#map.set(XYZEncoder.TEMPERATURE, CBOR.Float(floatVal));
24 | return this;
25 | }
26 |
27 | setGreeting = function(stringVal) {
28 | this.#map.set(XYZEncoder.GREETING, CBOR.String(stringVal));
29 | return this;
30 | }
31 |
32 | encode = function() {
33 | assertTrue("incomplete", this.#map.length == 3);
34 | return this.#map.encode();
35 | }
36 | }
37 |
38 | let cbor = new XYZEncoder()
39 | .setCounter(2)
40 | .setGreeting('Hi!')
41 | .setTemperature(53.0001)
42 | .encode();
43 |
44 | assertTrue("bad code", CBOR.toHex(cbor) == 'a3010202fb404a800346dc5d640363486921');
45 |
46 | success();
47 |
--------------------------------------------------------------------------------
/test/base64.js:
--------------------------------------------------------------------------------
1 | // Testing the B64U/B64 converters
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | let bin = new Uint8Array(256);
6 | for (let i = 0; i < bin.length; i++) {
7 | bin[i] = i;
8 | }
9 | let b64U = CBOR.toBase64Url(bin);
10 | assertFalse("cmp1", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64U)));
11 |
12 | // This is what "btoa" returns for bin:
13 | let b64 = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissL\
14 | S4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY\
15 | 2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYm\
16 | ZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz\
17 | 9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==';
18 |
19 | // fromBase64Url is "permissive" and takes Base64 with padding as well...
20 | assertFalse("cmp2", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64)));
21 |
22 | assertFalse("cmp3", CBOR.compareArrays(CBOR.fromBase64Url('oQVkZGF0YQ'),
23 | CBOR.fromHex('a1056464617461')));
24 | // Zero data is compliant
25 | assertFalse("cmp4", CBOR.compareArrays(CBOR.fromBase64Url(''), new Uint8Array()));
26 | assertTrue("cmp4", CBOR.toBase64Url(new Uint8Array()) == "");
27 | success();
28 |
--------------------------------------------------------------------------------
/test/nondeterministic.js:
--------------------------------------------------------------------------------
1 | // Testing "deterministic" code checks
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function oneTurn(hex, dn) {
6 | try {
7 | CBOR.decode(CBOR.fromHex(hex));
8 | throw Error("Should not fail on: " + dn);
9 | } catch (error) {
10 | if (!error.toString().includes("Non-d")) {
11 | throw error;
12 | }
13 | }
14 | let object = CBOR.initDecoder(CBOR.fromHex(hex),
15 | dn.includes("{") ? CBOR.LENIENT_MAP_DECODING : CBOR.LENIENT_NUMBER_DECODING).decodeWithOptions();
16 | if (object.toString() != dn || !object.equals(CBOR.decode(object.encode()))) {
17 | throw Error("non match:" + dn);
18 | }
19 | }
20 |
21 | oneTurn('1900ff', '255');
22 | oneTurn('1817', '23');
23 | oneTurn('A2026374776F01636F6E65', '{\n 1: "one",\n 2: "two"\n}');
24 | oneTurn('FB7FF8000000000000', 'NaN');
25 | oneTurn('FA7FC00000', 'NaN');
26 | oneTurn('FB3ff0000000000000', '1.0');
27 | oneTurn('c2480100000000000000', '72057594037927936');
28 | oneTurn('c24900ffffffffffffffff', '18446744073709551615');
29 | oneTurn('c240', '0');
30 |
31 | // This one is actually deterministic...
32 | try {
33 | oneTurn('fa7f7fffff', '3.4028234663852886e+38');
34 | } catch (error) {
35 | if (!error.toString().includes('Should not')) {
36 | throw error;
37 | }
38 | }
39 |
40 | success();
41 |
--------------------------------------------------------------------------------
/test/xyz-decoder.js:
--------------------------------------------------------------------------------
1 | // Simple "decoder" API
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | class XYZDecoder {
6 |
7 | static COUNTER = CBOR.Int(1);
8 | static TEMPERATURE = CBOR.Int(2);
9 | static GREETING = CBOR.Int(3);
10 |
11 | #counter;
12 | #temperature;
13 | #greeting;
14 |
15 | constructor(cbor) {
16 | // There MUST be exactly three key/value pairs.
17 | // CBOR data items are type-checked as well.
18 | let map = CBOR.decode(cbor);
19 | // If the top-level object is not a CBOR map, the next
20 | // JavaScript line will throw an exception because there is
21 | // only one get-method that has a CBOR wrapper as input parameter.
22 | this.#counter = map.get(XYZDecoder.COUNTER).getUint8();
23 | this.#temperature = map.get(XYZDecoder.TEMPERATURE).getFloat64();
24 | this.#greeting = map.get(XYZDecoder.GREETING).getString();
25 | // We got more than we asked for?
26 | map.checkForUnread();
27 | }
28 |
29 | get counter() {
30 | return this.#counter;
31 | }
32 |
33 | get temperature() {
34 | return this.#temperature;
35 | }
36 |
37 | get greeting() {
38 | return this.#greeting;
39 | }
40 |
41 | }
42 |
43 | let cbor = CBOR.fromHex('a3010202fb404a800346dc5d640363486921');
44 |
45 | let xyz = new XYZDecoder(cbor);
46 |
47 | assertTrue("counter", xyz.counter == 2);
48 | assertTrue("temperature", xyz.temperature == 53.0001);
49 | assertTrue("greeting", xyz.greeting == 'Hi!');
50 |
51 | success();
52 |
--------------------------------------------------------------------------------
/test/int-ranges.js:
--------------------------------------------------------------------------------
1 | // Testing range-constrained integers
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function goodRun(method, value) {
6 | let bigFlag = method.indexOf("64") > 0;
7 | let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
8 | let test = 'assertTrue("good", wrapper.' + method + '() == ' + (bigFlag ? value + 'n' : Number(value)) + ')';
9 | eval(test);
10 | }
11 |
12 | function badRun(method, value) {
13 | let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
14 | let test = 'wrapper.' + method + '()';
15 | try {
16 | eval(test);
17 | assertTrue("Should fail", false);
18 | } catch (error) {
19 | if (!error.toString().includes('Value out of range:') &&
20 | !error.toString().includes('Number.MAX_SAFE_INTEGER')) {
21 | throw error;
22 | }
23 | }
24 | }
25 |
26 | function innerTurn(method, signed, size) {
27 | let min = signed ? -(1n << BigInt(size) - 1n) : 0n;
28 | let max = signed ? (1n << BigInt(size) - 1n) - 1n : (1n << BigInt(size)) - 1n;
29 | if (size == 53) {
30 | max = BigInt(Number.MAX_SAFE_INTEGER);
31 | min = -max;
32 | }
33 | goodRun(method, min);
34 | goodRun(method, max);
35 | goodRun(method, 10n);
36 | badRun(method, max + 1n);
37 | badRun(method, min - 1n);
38 | }
39 |
40 | function oneTurn(size) {
41 | innerTurn("getInt" + size, true, size);
42 | if (size != 53) {
43 | innerTurn("getUint" + size, false, size);
44 | }
45 | }
46 |
47 | oneTurn(8);
48 | oneTurn(16);
49 | oneTurn(32);
50 | oneTurn(53);
51 | oneTurn(64);
52 |
53 | success();
54 |
--------------------------------------------------------------------------------
/test/tags.js:
--------------------------------------------------------------------------------
1 | // Testing "tag"
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | let object = CBOR.Array().add(CBOR.String("https://example.com/myobject")).add(CBOR.Int(6));
6 | let cbor = CBOR.Tag(CBOR.Tag.TAG_COTX, object).encode();
7 | let tag = CBOR.decode(cbor);
8 | assertTrue("t3", tag.getTagNumber()== CBOR.Tag.TAG_COTX);
9 | assertTrue("t3.1", object.equals(tag.get()));
10 | assertTrue("t3.2", tag.cotxId == "https://example.com/myobject");
11 | assertTrue("t3.3", tag.cotxObject.equals(CBOR.Int(6)));
12 | cbor = CBOR.Tag(0xf0123456789abcden, object).encode();
13 | assertTrue("t14", CBOR.decode(cbor).getTagNumber()== 0xf0123456789abcden);
14 | assertTrue("t5", CBOR.toHex(cbor) ==
15 | "dbf0123456789abcde82781c68747470733a2f2f6578616d706c652e636f6d2f6d796f626a65637406");
16 |
17 | [-1n, 0x10000000000000000n].forEach(tagNumber => {
18 | try {
19 | CBOR.Tag(tagNumber, CBOR.String("any"));
20 | throw Error("Should not");
21 | } catch (error) {
22 | if (!error.toString().includes("out of range")) {
23 | throw error;
24 | }
25 | }
26 | });
27 |
28 | [2n, 3n].forEach(tagNumber => {
29 | try {
30 | CBOR.Tag(tagNumber, CBOR.String("any"));
31 | throw Error("Should not");
32 | } catch (error) {
33 | if (!error.toString().includes("'bigint'")) {
34 | throw error;
35 | }
36 | }
37 | });
38 |
39 | [0n, 1n].forEach(tagNumber => {
40 | try {
41 | CBOR.Tag(tagNumber, CBOR.Boolean(true));
42 | throw Error("Should not");
43 | } catch (error) {
44 | if (!error.toString().includes("got: CBOR.Boolean")) {
45 | throw error;
46 | }
47 | }
48 | });
49 |
50 | success();
51 |
--------------------------------------------------------------------------------
/test/miscellaneous.js:
--------------------------------------------------------------------------------
1 | // miscellaneous tests
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | let bin = new Uint8Array([0xa5, 0x01, 0xd9, 0x01, 0xf4, 0x81, 0x18, 0x2d, 0x02, 0xf9, 0x80, 0x10,
6 | 0x04, 0x64, 0x53, 0x75, 0x72, 0x65, 0x05, 0xa2, 0x08, 0x69, 0x59, 0x65,
7 | 0x0a, 0x01, 0x61, 0x68, 0xe2, 0x82, 0xac, 0x09, 0x85, 0x66, 0x42, 0x79,
8 | 0x74, 0x65, 0x73, 0x21, 0x45, 0x01, 0x02, 0x03, 0x04, 0x05, 0xf5, 0xf4,
9 | 0xf6, 0x06, 0xc2, 0x4b, 0x66, 0x1e, 0xfd, 0xf2, 0xe3, 0xb1, 0x9f, 0x7c,
10 | 0x04, 0x5f, 0x15]);
11 |
12 | let cbor = CBOR.Map()
13 | .set(CBOR.Int(5),
14 | CBOR.Map()
15 | .set(CBOR.Int(8), CBOR.String("Ye\n\u0001ah€"))
16 | .set(CBOR.Int(9),
17 | CBOR.Array()
18 | .add(CBOR.String("Bytes!"))
19 | .add(CBOR.Bytes(new Uint8Array([1,2,3,4,5])))
20 | .add(CBOR.Boolean(true))
21 | .add(CBOR.Boolean(false))
22 | .add(CBOR.Null())))
23 | .set(CBOR.Int(4), CBOR.String("Sure"))
24 | .set(CBOR.Int(2), CBOR.Float(-9.5367431640625e-7))
25 | .set(CBOR.Int(6), CBOR.BigInt(123456789123456789123456789n))
26 | .set(CBOR.Int(1), CBOR.Tag(500n, CBOR.Array().add(CBOR.Int(45)))).encode();
27 | assertFalse("cmp1", CBOR.compareArrays(bin, cbor));
28 | let array = CBOR.decode(cbor).get(CBOR.Int(5)).get(CBOR.Int(9));
29 | assertTrue("bool1", array.get(2).getBoolean());
30 | assertFalse("bool1", array.get(3).getBoolean());
31 | assertFalse("null1", array.get(3).isNull());
32 | assertTrue("null2", array.get(4).isNull());
33 | assertFalse("cmp2", CBOR.compareArrays(CBOR.diagDecode(CBOR.decode(cbor).toString()).encode(), bin));
34 |
35 | assertTrue("version", CBOR.version == "1.0.16");
36 |
37 | success();
38 |
--------------------------------------------------------------------------------
/test/maps.js:
--------------------------------------------------------------------------------
1 | // Testing map operations
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { fail, assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | let map = CBOR.Map()
6 | .set(CBOR.Int(3), CBOR.String("three"))
7 | .set(CBOR.Int(4), CBOR.String("four"));
8 | assertTrue("size-0", map.length == 2);
9 | let keys = map.getKeys();
10 | assertTrue("size-1", keys.length == 2);
11 | assertTrue("get-0", map.get(keys[0]).getString() == "three");
12 | assertTrue("get-1", map.get(keys[1]).getString() == "four");
13 |
14 | assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
15 | assertTrue("size-2", map.length == 1);
16 | assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
17 | assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
18 | assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
19 | assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
20 | map = map.merge(
21 | CBOR.Map().set(CBOR.Int(1), CBOR.String("hi")).set(CBOR.Int(5), CBOR.String("yeah")));
22 | assertTrue("size-3", map.length == 3);
23 | assertTrue("merge-0", map.get(CBOR.Int(1)).getString() == "hi");
24 | assertTrue("upd-0", map.update(CBOR.Int(1), CBOR.BigInt(-8n), true).getString() == "hi");
25 | assertTrue("upd-1", map.get(CBOR.Int(1)).getBigInt() == -8n);
26 | assertTrue("upd-2", map.update(CBOR.Int(10), CBOR.BigInt(-8n), false) == null);
27 | assertTrue("upd-3", map.get(CBOR.Int(10)).getBigInt() == -8n);
28 |
29 | function badKey(js) {
30 | try {
31 | eval(js);
32 | fail("Must fail!");
33 | } catch (error) {
34 | if (!error.toString().includes('Map key')) {
35 | throw error;
36 | }
37 | }
38 | }
39 |
40 | let immutableKey1 = CBOR.Array();
41 | let immutableKey2 = CBOR.Array();
42 | CBOR.Map().set(immutableKey1, CBOR.Int(4));
43 | badKey("immutableKey1.add(CBOR.Int(6))");
44 | let mutableValue = CBOR.Array();
45 | CBOR.Map().set(CBOR.Int(5), mutableValue);
46 | mutableValue.add(CBOR.Map());
47 | CBOR.Map().set(CBOR.Array().add(immutableKey2), CBOR.Int(5));
48 | badKey("immutableKey2.add(CBOR.Int(6))");
49 |
50 | success();
51 |
--------------------------------------------------------------------------------
/doc/release-notes.txt:
--------------------------------------------------------------------------------
1 | 1.0.0 - 1.0.1:
2 | Beta version
3 |
4 | 2023-10-14 - 1.0.2:
5 | First release
6 |
7 | 2023-12-05 - 1.0.3:
8 | Improved map key handling through binary search and insert
9 | Added CBOR.Map.setSortingMode(flag) to cope with huge maps
10 |
11 | 2024-07-21 - 1.0.4:
12 | Signed and unsigned 8, 16, 32, and 64-bit integer get() methods added
13 | CBOR.Float.getFloat() renamed to getFloat64()
14 | CBOR.Float.getFloat32() method added
15 |
16 | 2024-08-19 - 1.0.5
17 | Completely refactored CBOR.initDecoder() and added a flag for "outlawing"
18 | NaN/Infinity as well as a Decoder.getByteCount() method
19 | Improved checking of CBOR items requirements against input buffer length
20 |
21 | 2024-08-20 - 1.0.6
22 | Updated documentation
23 |
24 | 2024-08-30 - 1.0.7
25 | Improved code
26 |
27 | 2024-09-25 - 1.0.8
28 | getFloat16() and CBOR.version added
29 |
30 | 2024-10-17 - 1.0.9
31 | Simplification: removed getMap(), getArray(), and getTag()
32 |
33 | 2025-01-02 - 1.0.10
34 | Added methods: CBOR.Array.update(), CBOR.Map.update(), CBOR.Map.merge(). CBOR.Tag.update()
35 | Changed method: CBOR.Tag.getTaggedObject() => CBOR.Tag.get()
36 | Changed method: Decoder.setNaNSupport() => Decoder.setFloatSupport()
37 |
38 | 2025-01-30 1.0.11
39 | "Outlawing" mutating key objects. See CBOR.Map().set()
40 |
41 | 2025-04-10 1.0.12
42 | Simplifying decoder options solution. See CBOR.initDecoder()
43 | Added getDateTime() and getEpochTime methods including in their tagged format
44 | Added argument number tests to core methods
45 | Added CBOR.Map.setDynamic() method
46 |
47 | 2025-06-29 1.0.13
48 | getInt() => getInt53()
49 | CBOR.REJECT_INVALID_FLOATS => CBOR.REJECT_NON_FINITE_FLOATS
50 | Added method: CBOR.Array.encodeAsSequence()
51 |
52 | 2025-07-18 1.0.14
53 | Added method: CBOR.nonFiniteFloatsMode()
54 |
55 | 2025-09-10 1.0.15
56 | Elimininated CBOR.REJECT_NON_FINITE_FLOATS and CBOR.nonFiniteFloatsMode()
57 | through the new wrapper object CBOR.NonFinite().
58 | Major update for non-finite numbers
59 | Dropped Tag.update()
60 | Added COTX properties to Tag
61 | Added remove() and insert() to Array
62 |
63 | 2025-12-05 1.0.16
64 | Added utility methods CBOR.createEpochTime() and CBOR.createDateTime()
--------------------------------------------------------------------------------
/test/float32.js:
--------------------------------------------------------------------------------
1 | // JavaScript source code
2 | import CBOR from '../npm/mjs/index.mjs';
3 |
4 | let float32 = 0;
5 | let float16 = 0;
6 | let runs = 0;
7 |
8 | function getF32(f32) {
9 | let b32 = CBOR.fromBigInt(f32);
10 | while (b32.length < 4) b32 = CBOR.addArrays(new Uint8Array([0]), b32);
11 | return new DataView(b32.buffer, 0, 4).getFloat32(0, false);
12 | }
13 |
14 | function convert(f32) {
15 | let regular = true;
16 | let simple = true;
17 | let cbor = null;
18 | let nf = null;
19 | try {
20 | if ((f32 & 0x7f800000n) == 0x7f800000n) {
21 | nf = CBOR.NonFinite(f32);
22 | regular = false;
23 | simple = nf.isSimple();
24 | cbor = nf.encode();
25 | }
26 | console.log(CBOR.toHex(CBOR.fromBigInt(f32)) + " V=" + (regular ? "G" : simple ? "S" : "X"));
27 | if (simple) {
28 | cbor = CBOR.Float.createExtendedFloat(getF32(f32)).encode();
29 | }
30 | switch (cbor.length) {
31 | case 3:
32 | float16++;
33 | break;
34 | case 5:
35 | float32++;
36 | break;
37 | default:
38 | throw new Error("BUG");
39 | }
40 | let object = CBOR.decode(cbor);
41 | // console.log((object instanceof CBOR.Float) + " " + object.toString());
42 | if (simple) {
43 | let d = getF32(f32);
44 | let v = object.getExtendedFloat64();
45 | if (v.toString() != d.toString()) {
46 | throw new Error ("Fail");
47 | }
48 | if (regular) {
49 | v = object.getFloat64();
50 | if (v.toString() != d.toString()) {
51 | throw new Error ("Fail2");
52 | }
53 | }
54 | } else {
55 | if ((((nf.getNonFinite64() >> 29n) ^ f32) & 0x7fffffn) != 0n) {
56 | throw new Error ("Fail3");
57 | }
58 | }
59 | ++runs;
60 |
61 | } catch (error) {
62 | console.log("**********=" + f32 + " e=" + error.toString());
63 | throw error;
64 | }
65 |
66 | }
67 |
68 | let f = 0n;
69 | while (f < 0x800000) {
70 | let e = 0n;
71 | while (e < 0x100000000n) {
72 | // console.log(CBOR.toHex(CBOR.fromBigInt(f)));
73 | convert(e + f);
74 | e += 0x800000n;
75 | }
76 | f = f ? f + f : 1n
77 | }
78 | console.log("Runs=" + runs);
79 |
--------------------------------------------------------------------------------
/test/arrays.js:
--------------------------------------------------------------------------------
1 | // Testing array operations
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success, fail } from './assertions.js';
4 |
5 | let array = CBOR.Array()
6 | .add(CBOR.String("three"))
7 | .add(CBOR.String("four"));
8 | assertTrue("size-0", array.length == 2);
9 | assertTrue("get-0", array.get(0).getString() == "three");
10 | assertTrue("get-1", array.get(1).getString() == "four");
11 | let arrayElements = array.toArray();
12 | assertTrue("size-1", arrayElements.length == 2);
13 | assertTrue("arr-0", arrayElements[0].getString() == "three");
14 | assertTrue("arr-1", arrayElements[1].getString() == "four");
15 | assertTrue("upd-1", array.update(1, CBOR.Int(1)).getString() == "four");
16 | assertTrue("upd-2", array.get(1).getInt8() == 1);
17 | assertTrue("size-1", array.length == 2);
18 | assertTrue("upd-3", array.get(0).getString() == "three");
19 | assertTrue("upd-4", array.insert(array.length, CBOR.Int(-8)) == array);
20 | assertTrue("upd-5", array.get(array.length - 1).equals(CBOR.Int(-8)));
21 | assertTrue("upd-4", array.insert(0, CBOR.Int(-9)) == array);
22 | assertTrue("upd-5", array.get(0).equals(CBOR.Int(-9)));
23 | let l = array.length;
24 | assertTrue("upd-6", array.remove(0).equals(CBOR.Int(-9)));
25 | assertTrue("upd-7", l == array.length + 1);
26 | assertTrue("upd-8", array.get(0).getString() == "three");
27 | assertTrue("upd-9", array.toDiag(false) == '["three",1,-8]');
28 |
29 | function aBadOne(expression) {
30 | try {
31 | eval("array." + expression);
32 | fail("Should not pass");
33 | } catch (Error) {
34 | }
35 | }
36 |
37 | aBadOne("get('string')")
38 | aBadOne("get(array.length)");
39 | aBadOne("get(-1)");
40 | aBadOne("insert(array.length + 1, CBOR.Int(6))");
41 | aBadOne("insert(array.length)");
42 | aBadOne("remove(array.length)");
43 | aBadOne("remove(array.length - 1, 'hi')");
44 | aBadOne("get(0, 6)");
45 |
46 | /*
47 | assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
48 | assertTrue("size-2", map.length == 1);
49 | assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
50 | assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
51 | assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
52 | assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
53 | */
54 |
55 | success();
56 |
--------------------------------------------------------------------------------
/test/integer.js:
--------------------------------------------------------------------------------
1 | // Test program for integer "edge cases"
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, fail, success } from './assertions.js';
4 |
5 | function oneTurn(value, expected) {
6 | let text = value.toString();
7 | while (text.length < 25) {
8 | text += ' ';
9 | }
10 | let cbor = CBOR.BigInt(value).encode();
11 | let got = CBOR.toHex(cbor);
12 | if (got != expected) {
13 | got = '***=' + got;
14 | } else {
15 | got = '';
16 | }
17 | assertTrue("Failed decoding: " + value, CBOR.decode(cbor).getBigInt() == value);
18 | while (expected.length < 20) {
19 | expected += ' ';
20 | }
21 | if (got.length) {
22 | fail(text + expected + got);
23 | }
24 | }
25 | // -0 is treated as 0 for integers
26 | assertTrue("minus-0", CBOR.toHex(CBOR.Int(-0).encode()) == "00");
27 | oneTurn(0n, '00');
28 | oneTurn(-1n, '20');
29 | oneTurn(255n, '18ff');
30 | oneTurn(256n, '190100');
31 | oneTurn(-256n, '38ff');
32 | oneTurn(-257n, '390100');
33 | oneTurn(1099511627775n, '1b000000ffffffffff');
34 | oneTurn(18446744073709551615n, '1bffffffffffffffff');
35 | oneTurn(18446744073709551616n, 'c249010000000000000000');
36 | oneTurn(-18446744073709551616n, '3bffffffffffffffff');
37 | oneTurn(-18446744073709551617n, 'c349010000000000000000');
38 |
39 | try {
40 | CBOR.Int(1.1);
41 | fail("Should not");
42 | } catch (error) {
43 | assertTrue("msg1", error.toString().includes("Invalid integer: 1.1"));
44 | }
45 | try {
46 | CBOR.Int(Number.MAX_SAFE_INTEGER + 1);
47 | fail("Should not");
48 | } catch (error) {
49 | assertTrue("msg1", error.toString().includes("Invalid integer: " + (Number.MAX_SAFE_INTEGER + 1)));
50 | }
51 | try {
52 | CBOR.Int("10");
53 | fail("Should not");
54 | } catch (error) {
55 | assertTrue("msg2", error.toString().includes("Argument is not a 'number'"));
56 | }
57 | try {
58 | CBOR.BigInt("10");
59 | fail("Should not");
60 | } catch (error) {
61 | assertTrue("msg3", error.toString().includes("Argument is not a 'bigint'"));
62 | }
63 | try {
64 | CBOR.BigInt(1n, 7);
65 | fail("Should not");
66 | } catch (error) {
67 | assertTrue("msg4", error.toString().includes("CBOR.BigInt expects 1 argument(s)"));
68 | }
69 | try {
70 | CBOR.Int(1, 7);
71 | fail("Should not");
72 | } catch (error) {
73 | assertTrue("msg4", error.toString().includes("CBOR.Int expects 1 argument(s)"));
74 | }
75 |
76 | success();
77 |
--------------------------------------------------------------------------------
/test/diagnostic.js:
--------------------------------------------------------------------------------
1 | // Testing "diagnostic notation"
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function oneTurn(cborText, ok, compareWithOrNull) {
6 | try {
7 | let compareText = compareWithOrNull ? compareWithOrNull : cborText;
8 | let result = CBOR.diagDecode(cborText);
9 | assertTrue("Should not", ok);
10 | let sequence = CBOR.diagDecodeSequence(cborText);
11 | if (result.toString() != compareText) {
12 | throw Error("input:\n" + cborText + "\nresult:\n" + result);
13 | }
14 | assertTrue("seq", sequence.length == 1);
15 | if (sequence[0].toString() != compareText) {
16 | throw Error("input:\n" + cborText + "\nresult:\n" + result);
17 | }
18 | } catch (error) {
19 | assertFalse("Err: " + error, ok);
20 | }
21 | }
22 |
23 | function oneBinaryTurn(diag, hex) {
24 | assertTrue("bin", CBOR.toHex(CBOR.diagDecode(diag).encode()) == hex);
25 | }
26 |
27 | oneTurn("2", true, null);
28 | oneTurn("2.0", true, null);
29 | oneTurn("123456789012345678901234567890", true, null);
30 | oneTurn("Infinity", true, null);
31 | oneTurn("-Infinity", true, null);
32 | oneTurn("NaN", true, null);
33 | oneTurn("0.0", true, null);
34 | oneTurn("-0.0", true, null);
35 | oneTurn('{\n 4: "hi"\n}', true, null);
36 | oneTurn('[4, true, false, null]', true, null);
37 | oneTurn('"next\nline\r\\\ncont\r\nk"', true, '"next\\nline\\ncont\\nk"');
38 | oneTurn('{1:<< 5 , 7 >>}', true, "{\n 1: h'0507'\n}");
39 | oneTurn('<<[3.0]>>', true, "h'81f94200'");
40 | oneTurn('0b100_000000001', true, "2049");
41 | oneTurn('4.0e+500', false, null);
42 | oneTurn('4.0e+5', true, "400000.0");
43 | oneTurn('"missing', false, null);
44 | oneTurn('simple(21)', true, 'true');
45 | oneTurn('simple(59)', true, 'simple(59)');
46 | oneBinaryTurn('"\\ud800\\udd51"', "64f0908591");
47 | oneBinaryTurn("'\\u20ac'", "43e282ac");
48 | oneBinaryTurn('"\\"\\\\\\b\\f\\n\\r\\t"', "67225c080c0a0d09");
49 |
50 | let cborObject = CBOR.decode(CBOR.fromHex('a20169746578740a6e6578740284fa3380000147a10564646\
51 | 17461a1f5f4c074323032332d30362d30325430373a35333a31395a'));
52 |
53 | let cborText = '{\n' +
54 | ' 1: "text\\nnext",\n' +
55 | ' 2: [\n' +
56 | ' 5.960465188081798e-8,\n' +
57 | ' h\'a1056464617461\',\n' +
58 | ' {\n' +
59 | ' true: false\n' +
60 | ' },\n' +
61 | ' 0("2023-06-02T07:53:19Z")\n' +
62 | ' ]\n' +
63 | '}';
64 |
65 | assertTrue("pretty", cborObject.toDiag(true) == cborText);
66 | assertTrue("oneline", cborObject.toDiag(false) ==
67 | cborText.replaceAll('\n', '').replaceAll(' ',''));
68 | assertTrue("parse", CBOR.diagDecode(cborText).equals(cborObject));
69 | let sequence = CBOR.diagDecodeSequence('45,{4:7}');
70 | assertTrue("seq2", sequence.length == 2);
71 | assertTrue("seq3", sequence[0].getInt32() == 45);
72 | assertTrue("seq4", sequence[1].equals(CBOR.Map().set(CBOR.Int(4),CBOR.Int(7))));
73 |
74 | try {
75 | CBOR.diagDecode("float'000000'");
76 | fail("bugf");
77 | } catch (error) {
78 | assertTrue("fp", error.toString().includes('floating-point'));
79 | }
80 |
81 | success();
82 |
--------------------------------------------------------------------------------
/test/node-crypto.mjs:
--------------------------------------------------------------------------------
1 | // Node.js + CBOR.js test
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { fail, success } from './assertions.js';
4 | const crypto = await import('node:crypto');
5 |
6 | // Application independent CSF constants
7 | const CSF_CONTAINER_LBL = CBOR.Simple(99);
8 | const CSF_ALG_LBL = CBOR.Int(1);
9 | const CSF_SIG_LBL = CBOR.Int(6);
10 |
11 | // COSE => Node.js algorithm translation
12 | const HASH_ALGORITHMS = new Map()
13 | .set(5, "sha256").set(6, "sha384").set(7, "sha512");
14 |
15 | function hmac(coseAlg, key, data) {
16 | let alg = HASH_ALGORITHMS.get(coseAlg);
17 | if (alg === undefined) throw "Unknown alg: " + coseAlg;
18 | return crypto.createHmac(alg, key).update(data).digest();
19 | }
20 |
21 | const SHARED_KEY = crypto.createSecretKey(
22 | '7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a', 'hex');
23 |
24 | const APP_P1_LBL = CBOR.Int(1); // Application label
25 | const APP_P2_LBL = CBOR.Int(2); // ""
26 |
27 | ////////////////////////////////////
28 | // Create an unsigned CBOR object //
29 | ////////////////////////////////////
30 | let object = CBOR.Map()
31 | .set(APP_P1_LBL, CBOR.String("data")) // Application data
32 | .set(APP_P2_LBL, CBOR.String("more data")); // ""
33 |
34 | ////////////////////////////////////////
35 | // Add a signature to the CBOR object //
36 | ////////////////////////////////////////
37 | const COSE_ALG = 5; // Selected HMAC algorithm
38 |
39 | let csf = CBOR.Map() // Create CSF container and
40 | .set(CSF_ALG_LBL, CBOR.Int(COSE_ALG)); // add COSE algorithm to it
41 | object.set(CSF_CONTAINER_LBL, csf); // Add CSF container to object
42 | let sig = hmac(COSE_ALG, // Generate signature over
43 | SHARED_KEY, // the current object
44 | object.encode()); // encode(): all we got so far
45 | csf.set(CSF_SIG_LBL, CBOR.Bytes(sig)); // Add signature to CSF container
46 | let cborBinary = object.encode(); // Return CBOR as an Uint8Array
47 |
48 | console.log(object.toString()); // Show in Diagnostic Notation
49 |
50 | /////////////////////////////////////
51 | // Validate the signed CBOR object //
52 | /////////////////////////////////////
53 | object = CBOR.decode(cborBinary); // Decode CBOR object
54 | csf = object.get(CSF_CONTAINER_LBL); // Get CSF container
55 | let alg = csf.get(CSF_ALG_LBL).getInt32(); // Get COSE algorithm
56 | let readSig = csf.remove(CSF_SIG_LBL).getBytes(); // Get and REMOVE signature value
57 | let actualSig = hmac(alg, // Calculate signature over
58 | SHARED_KEY, // the current object
59 | object.encode()); // encode(): all but the signature
60 | if (CBOR.compareArrays(readSig, actualSig)) { // HMAC validation
61 | fail("Signature did not validate");
62 | }
63 | // Validated object, access the "payload":
64 | let p1 = object.get(APP_P1_LBL).getString(); // p1 should now contain "data"
65 |
66 | success();
67 |
--------------------------------------------------------------------------------
/test/check-for-unread.js:
--------------------------------------------------------------------------------
1 | // Testing the "checkForUnread()" feature
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function oneTurn(create, access, errorString) {
6 | let res = eval(create);
7 | try {
8 | res.checkForUnread();
9 | if (errorString !== null) {
10 | throw Error("no way");
11 | }
12 | } catch (error) {
13 | if (!error.toString().includes('never read')) {
14 | throw error;
15 | }
16 | }
17 | try {
18 | eval(access);
19 | res.checkForUnread();
20 | assertFalse("cfu1", errorString);
21 | } catch (error) {
22 | assertTrue("cfu2=" + error, errorString);
23 | if (!error.toString().includes(errorString)) {
24 | throw error;
25 | }
26 | }
27 | eval(create).scan().checkForUnread();
28 | }
29 |
30 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
31 | "res.get(0).get(CBOR.Int(1)).getString()");
32 |
33 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
34 | "res",
35 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
36 |
37 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
38 | "res.get(0).get(CBOR.Int(1))",
39 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
40 |
41 | oneTurn("CBOR.Array().add(CBOR.Map())",
42 | "res",
43 | "Array element of type=CBOR.Map with value={} was never read");
44 |
45 | // Empty Map => nothing to read
46 | oneTurn("CBOR.Array().add(CBOR.Map())",
47 | "res.get(0)",
48 | "Array element of type=CBOR.Map with value={} was never read");
49 |
50 | oneTurn("CBOR.Array().add(CBOR.Map())",
51 | "res.get(0).scan()",
52 | null);
53 |
54 | // Empty Array => nothing to read
55 | oneTurn("CBOR.Array()",
56 | "res",
57 | "Data of type=CBOR.Array with value=[] was never read");
58 |
59 | oneTurn("CBOR.Array()",
60 | "res.scan()",
61 | null);
62 |
63 | oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
64 | "res.get().get(CBOR.Int(1)).getString()");
65 |
66 | oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
67 | "res.get()",
68 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
69 |
70 | oneTurn("CBOR.Tag(8n, CBOR.Map())",
71 | "res.get()",
72 | "Tagged object 8 of type=CBOR.Map with value={} was never read");
73 |
74 | oneTurn("CBOR.Simple(8)",
75 | "res",
76 | "Data of type=CBOR.Simple with value=simple(8) was never read");
77 |
78 | oneTurn("CBOR.Simple(8)",
79 | "res.getSimple()",
80 | null);
81 |
82 | oneTurn("CBOR.Tag(8n, CBOR.Map())",
83 | "res.get().scan()",
84 | null);
85 |
86 | // Date time specials
87 | oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
88 | "res.get()",
89 | "Tagged object 0 of type=CBOR.String with value=\"2025-02-20T14:09:08Z\" was never read");
90 |
91 | oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
92 | "res.get().getString()",
93 | null);
94 |
95 | oneTurn("CBOR.Tag(8n, CBOR.Int(2))",
96 | "res.get()",
97 | "Tagged object 8 of type=CBOR.Int with value=2 was never read");
98 |
99 | oneTurn("CBOR.Int(1)",
100 | "res.getInt32()");
101 | success();
102 |
--------------------------------------------------------------------------------
/test/map-performance.js:
--------------------------------------------------------------------------------
1 | // Test program measuring map performance
2 |
3 | import CBOR from '../npm/mjs/index.mjs';
4 | import { assertTrue, assertFalse, success } from './assertions.js';
5 |
6 | const TOTAL_SET_OPERATIONS = 200000;
7 |
8 | const SMALL_MAP = 10;
9 | const MEDIUM_MAP = 50;
10 |
11 | let SORTED_KEYS = [TOTAL_SET_OPERATIONS];
12 | let VALUES = [TOTAL_SET_OPERATIONS];
13 |
14 | for (let q = 0; q < TOTAL_SET_OPERATIONS; q++) {
15 | let key = CBOR.Int(q);
16 | SORTED_KEYS[q] = key;
17 | VALUES[q] = CBOR.Int(q);
18 | }
19 |
20 | function printTime(label, mapSize, startTime, sortFlag) {
21 | console.log(`${label}(${mapSize}) ${sortFlag ? "sorted" : "unsorted"}\
22 | map execution time=${new Date().getTime() - startTime.getTime()} ms`);
23 | }
24 |
25 | function bigMap(sortFlag) {
26 | let startTime = new Date();
27 | let map = CBOR.Map().setSortingMode(sortFlag);
28 |
29 | for (let q = 0; q < TOTAL_SET_OPERATIONS; q++) {
30 | map.set(SORTED_KEYS[q], VALUES[q]);
31 | }
32 | printTime("SET", TOTAL_SET_OPERATIONS, startTime, sortFlag);
33 |
34 | startTime = new Date();
35 | for (let n = 0; n < TOTAL_SET_OPERATIONS ; n++) {
36 | if (map.get(SORTED_KEYS[n]).getInt32() != n) {
37 | throw Error("Big access");
38 | }
39 | }
40 | printTime("GET", TOTAL_SET_OPERATIONS, startTime, sortFlag);
41 |
42 | if (sortFlag) return;
43 |
44 | startTime = new Date();
45 | map = CBOR.Map();
46 | for (let n = TOTAL_SET_OPERATIONS; --n >= 0;) {
47 | map.set(SORTED_KEYS[n], VALUES[n]);
48 | }
49 | printTime("Reverse SET", TOTAL_SET_OPERATIONS, startTime, sortFlag);
50 | }
51 |
52 | function multipleSmallMaps(mapSize, sortFlag) {
53 | let map = null;
54 | let startTime = new Date();
55 | let maps = TOTAL_SET_OPERATIONS / mapSize;
56 |
57 | for (let q = 0; q < maps; q++) {
58 | // Creating a CBORMap object is a heavy operation
59 | map = CBOR.Map().setSortingMode(sortFlag);
60 | for (let n = 0; n < mapSize; n++) {
61 | map.set(SORTED_KEYS[n], VALUES[n]);
62 | }
63 | }
64 | printTime("SET", mapSize, startTime, sortFlag);
65 |
66 | startTime = new Date();
67 | for (let q = 0; q < maps; q++) {
68 | for (let n = 0; n < mapSize; n++) {
69 | if (map.get(SORTED_KEYS[n]).getInt32() != n) {
70 | throw Error("Medium access");
71 | }
72 | }
73 | }
74 | printTime("GET", mapSize, startTime, sortFlag);
75 |
76 | startTime = new Date();
77 | for (let q = 0; q < maps; q++) {
78 | map = [];
79 | for (let n = 0; n < mapSize; n++) {
80 | let key = SORTED_KEYS[n];
81 | let entry = {"key": key,
82 | "value": VALUES[n],
83 | "encoded": key.encode()};
84 | if (n) {
85 | if (CBOR.compareArrays(map[n - 1].encoded, entry.encoded) > 0) {
86 | throw Error("ordering or duplicate");
87 | }
88 | }
89 | map[n] = entry;
90 | }
91 | }
92 | printTime("SET naked-map", mapSize, startTime, sortFlag);
93 |
94 | if (sortFlag) return;
95 |
96 | startTime = new Date();
97 | for (let q = 0; q < maps; q++) {
98 | // Creating a CBORMap object is a heavy operation
99 | map = CBOR.Map();
100 | for (let n = mapSize; --n >= 0;) {
101 | map.set(SORTED_KEYS[n], VALUES[n]);
102 | }
103 | }
104 | printTime("Reverse SET", mapSize, startTime, sortFlag);
105 | }
106 |
107 | multipleSmallMaps(SMALL_MAP, true);
108 | multipleSmallMaps(SMALL_MAP, false);
109 |
110 | multipleSmallMaps(MEDIUM_MAP, true);
111 | multipleSmallMaps(MEDIUM_MAP, false);
112 |
113 | bigMap(true);
114 | bigMap(false);
115 |
116 | success();
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | This repository contains a
4 | [CBOR JavaScript API](https://cyberphone.github.io/CBOR.js/doc/).
5 | The API loosely mimics the "JSON" object by _exposing a single global object_,
6 | unsurprisingly named "CBOR". To minimize the need for application developers
7 | having detailed knowledge of CBOR,
8 | the API provides a set of high level CBOR
9 | [Wrapper Objects](https://cyberphone.github.io/CBOR.js/doc/#main.wrappers)
10 | which also serve as "bridge" between CBOR and JavaScript.
11 |
12 | The wrapper objects are used for encoding CBOR data items,
13 | as well as being the result of CBOR decoding.
14 |
15 | ### Design Rationale
16 |
17 | The described API builds on the
18 | [CBOR::Core](https://www.ietf.org/archive/id/draft-rundgren-cbor-core-19.html)
19 | platform profile.
20 |
21 | Due to the desire maintaining interoperability across different platforms,
22 | the API "by design" does not address JavaScript specific
23 | types like binary data beyond `Uint8Array`.
24 | Also see: [CBOR Everywhere](https://github.com/cyberphone/cbor-everywhere/).
25 |
26 | ### "CBOR" Components
27 | - Self-encoding wrapper objects
28 | - Decoder
29 | - Diagnostic Notation decoder
30 | - Utility functions
31 |
32 | ### Encoding Example
33 |
34 | ```javascript
35 | let cbor = CBOR.Map()
36 | .set(CBOR.Int(1), CBOR.Float(45.7))
37 | .set(CBOR.Int(2), CBOR.String("Hi there!")).encode();
38 |
39 | console.log(CBOR.toHex(cbor));
40 | ------------------------------
41 | a201fb4046d9999999999a0269486920746865726521
42 | ```
43 | Note: there are no requirements "chaining" objects as shown above; items
44 | may be added to [CBOR.Map](https://cyberphone.github.io/CBOR.js/doc/#wrapper.cbor.map)
45 | and [CBOR.Array](https://cyberphone.github.io/CBOR.js/doc/#wrapper.cbor.array) objects in separate steps.
46 |
47 | ### Decoding Example
48 |
49 | ```javascript
50 | let map = CBOR.decode(cbor);
51 | console.log(map.toString()); // Diagnostic notation
52 | ----------------------------------------------------
53 | {
54 | 1: 45.7,
55 | 2: "Hi there!"
56 | }
57 |
58 | console.log('Value=' + map.get(CBOR.Int(1)).getFloat64());
59 | ----------------------------------------------------------
60 | Value=45.7
61 | ```
62 |
63 | ### On-line Testing
64 |
65 | On https://cyberphone.github.io/CBOR.js/doc/playground.html you will find a simple Web application,
66 | permitting testing the encoder, decoder, and diagnostic notation implementation.
67 |
68 | ### NPM Version
69 |
70 | For usage with Node.js and Deno, a NPM version is available: https://npmjs.com/package/cbor-object
71 |
72 | ### Deterministic Encoding
73 |
74 | For maintaining cross-platform interoperability, CBOR.js implements
75 | [deterministic encoding](https://cyberphone.github.io/CBOR.js/doc/index.html#main.deterministic).
76 |
77 | To shield developers from having to know the inner workings of deterministic encoding, CBOR.js performs
78 | all necessary transformations _automatically_. This for example means that if the
79 | [set()](https://cyberphone.github.io/CBOR.js/doc/#cbor.map.set) operations
80 | in the [Encoding Example](#encoding-example) were swapped, the generated CBOR would still be the same.
81 |
82 | ### Diagnostic Notation Support
83 |
84 | To simplify _logging_, _documentation_, and _debugging_, CBOR.js includes support for
85 | [diagnostic notation](https://cyberphone.github.io/CBOR.js/doc/index.html#main.diagnostic).
86 |
87 | However, diagnostic notation can also be used as _input_ for creating CBOR based _test data_ and
88 | _configuration files_ from text:
89 | ```javascript
90 | let cbor = CBOR.diagDecode(`{
91 | # Comments are also permitted
92 | 1: 45.7,
93 | 2: "Hi there!"
94 | }`).encode();
95 |
96 | console.log(CBOR.toHex(cbor));
97 | ------------------------------
98 | a201fb4046d9999999999a0269486920746865726521
99 | ```
100 | Aided by the model used for deterministic encoding, diagnostic notation becomes _bidirectional,_
101 | while remaining faithful to the native CBOR representation.
102 |
103 | ### Other Compatible Implementations
104 |
105 | |Language|URL|
106 | |-|-|
107 | |JDK 21+|https://github.com/cyberphone/openkeystore|
108 | |Android/Java|https://github.com/cyberphone/android-cbor|
109 |
110 | Updated: 2025-10-01
111 |
--------------------------------------------------------------------------------
/doc/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin:10pt;
3 | font-size:10pt;
4 | font-family:Verdana,'Bitstream Vera Sans','DejaVu Sans',Arial,'Liberation Sans';
5 | }
6 |
7 | a {
8 | color:#007fdd;
9 | text-decoration:none;
10 | font-weight:500;
11 | outline:none;
12 | }
13 |
14 | h3 {
15 | font-size: 1.3em;
16 | font-weight:600;
17 | }
18 |
19 | code, kbd {
20 | font-family:'DejaVu Sans Mono', monospace;
21 | font-size: 1.0em;
22 | }
23 |
24 | kbd {
25 | color:maroon;
26 | }
27 |
28 | h5 {
29 | font-size: 1.1em;
30 | margin-bottom:0.5em;
31 | }
32 |
33 | li {
34 | padding-top:5pt;
35 | }
36 |
37 | .staticbox, .textbox {
38 | box-sizing:border-box;
39 | font-size:1.0em;
40 | font-family: monospace;
41 | width:100%;word-break:break-all;
42 | border-width:1px;
43 | border-style:solid;
44 | border-color:grey;
45 | padding:10pt;
46 | }
47 |
48 | .staticbox {
49 | background:#f8f8f8;
50 | }
51 |
52 | .textbox {
53 | background:#ffffea;
54 | }
55 |
56 | .header {
57 | text-align:center;
58 | font-weight:bolder;
59 | font-size:12pt;
60 | font-family:arial,verdana;
61 | }
62 |
63 | .sitefooter {
64 | display:flex;
65 | align-items:center;
66 | border-width:1px 0 0 0;
67 | border-style:solid;
68 | position:absolute;
69 | z-index:5;
70 | left:0px;
71 | bottom:0px;
72 | right:0px;
73 | padding:0.5em 0.7em;
74 | }
75 |
76 | .stdbtn, .multibtn {
77 | cursor:pointer;
78 | background:linear-gradient(to bottom, #eaeaea 14%,#fcfcfc 52%,#e5e5e5 89%);
79 | border-width:1px;
80 | border-style:solid;
81 | border-color:#a9a9a9;
82 | border-radius:5pt;
83 | padding:3pt 10pt;
84 | }
85 |
86 | .sigparmbox {
87 | border-radius:5pt;
88 | padding:0 5pt 5pt 5pt;
89 | }
90 |
91 | .sigparmhead {
92 | border-radius:3pt;
93 | padding:3pt 5pt;
94 | z-index:5;
95 | position:relative;
96 | top:-10pt;
97 | margin-bottom:-3pt;
98 | }
99 |
100 | .defbtn {
101 | border-radius:2pt;
102 | display:inline-block;
103 | cursor:pointer;
104 | padding:1pt 3pt;
105 | background-color:#f0f5fd;
106 | border-color:#2a53ea;
107 | }
108 |
109 | .sigparmbox, .sigparmhead, .defbtn {
110 | border-width:1px;
111 | border-style:solid;
112 | }
113 |
114 | .sigparmhead, .sitefooter {
115 | border-color:#c85000;
116 | background-color:#fffcfc;
117 | }
118 |
119 | .sigparmbox {
120 | border-color:#008040;
121 | background-color:#fbfff7;
122 | }
123 |
124 | .stdbtn, .multibtn, .sigparmhead {
125 | font-family:Arial,'Liberation Sans',Verdana,'Bitstream Vera Sans','DejaVu Sans';
126 | font-size:10pt;
127 | }
128 |
129 | .stdbtn, .multibtn, .sigparmbox, .staticbox, .textbox {
130 | box-shadow:3pt 3pt 3pt #d0d0d0;
131 | }
132 |
133 | .stdbtn {
134 | display:inline-block;
135 | }
136 |
137 | .stdbtn {
138 | margin-top:15pt;
139 | }
140 |
141 | .multibtn {
142 | margin-top:12pt;
143 | text-align:center;
144 | }
145 |
146 |
147 | .webpkitable {
148 | display: inline-block;
149 | border-collapse: collapse;
150 | box-shadow: 0.2em 0.2em 0.2em #d0d0d0;
151 | margin: 1em 1em 1em 0;
152 | }
153 |
154 | .webpkitable td {
155 | background-color: #fffdf2;
156 | padding: 0.4em 0.5em;
157 | border-width: 1px;
158 | border-style: solid;
159 | border-color: black;
160 | }
161 |
162 | .webpkitable th {
163 | padding: 0.4em 0.5em;
164 | background-color: #f8f8f8;
165 | text-align: center;
166 | font-family: sans-serif;
167 | font-size: 1.1em;
168 | font-weight: bolder;
169 | border-width: 1px;
170 | border-style: solid;
171 | border-color: black;
172 | }
173 |
174 | .webpkisvg, .webpkibox, .webpkicomment, .webpkihexbox {
175 | display: inline-block;
176 | box-shadow: 0.2em 0.2em 0.2em #d0d0d0;
177 | border-width: 1px;
178 | border-style: solid;
179 | border-color: black;
180 | }
181 |
182 | .webpkicomment {
183 | padding: 0.5em 1em;
184 | background-color: #fafffa
185 | }
186 |
187 | .webpkilistspacing {
188 | padding-bottom: 0.7em;
189 | }
190 |
191 | .webpkibox, .webpkihexbox {
192 | padding: 0.8em 1em;
193 | font-family: "Noto Mono",monospace;
194 | margin: 1em;
195 | }
196 |
197 | .webpkisvg {
198 | margin: 0 1em 1em 0;
199 | }
200 |
201 | .webpkibox {
202 | white-space: nowrap;
203 | }
204 |
205 | .webpkifloat {
206 | overflow-x: auto;
207 | }
208 |
209 | .webpkihexbox {
210 | word-break: break-all;
211 | }
212 |
213 | .webpkicode {
214 | color: maroon;
215 | }
216 |
217 | .webpkihighlite {
218 | background: black;
219 | color: white;
220 | }
221 |
222 | .webpkidiv {
223 | background-color:white !important;
224 | font-size:0.6em;
225 | }
226 |
227 | @media (max-width:768px) {
228 |
229 | .stdbtn, .multibtn, .sigparmbox, .staticbox, .textbox {
230 | box-shadow:2pt 2pt 2pt #d0d0d0;
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/doc/cbor.js.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | CBOR.js Logotype
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/test/number-table.mjs:
--------------------------------------------------------------------------------
1 | // int-table.mjs
2 | "use strict";
3 |
4 | import { argv, exit } from 'node:process';
5 | import CBOR from '../../CBOR.js/npm/mjs/index.mjs';
6 | let args = process.argv.slice(2);
7 | if (args.length != 2 ||
8 | !(args[0] == "RFC" || args[0] == "HTML") ||
9 | !(args[1] == "INT" || args[1] == "FLOAT")) {
10 | console.log("arguments: RFC|HTML INT|FLOAT");
11 | exit();
12 | }
13 | let rfcMode = args[0] == "RFC";
14 | let intMode = args[1] == "INT";
15 | console.log("Mode: " + args[0]);
16 |
17 | let table = rfcMode ? `
\n` + (intMode ? "Integers" : "Floating-Point Numbers") +
18 | `
19 |
20 | Diagnostic Notation
21 | CBOR Encoding
22 | Comment
23 |
24 | \n`: ``;
25 |
26 | function oneTurn(numberText, cborHex, comment) {
27 | let cbor = CBOR.diagDecode(numberText);
28 | if (cbor.toString() != numberText) {
29 | throw numberText;
30 | }
31 | let bin = CBOR.decode(CBOR.fromHex(cborHex));
32 | if (CBOR.compareArrays(bin.encode(), cbor.encode())) {
33 | throw cborHex;
34 | }
35 | table += rfcMode ? '' +
36 | numberText +
37 | ' \n' + cborHex +
38 | ' \n' + comment + ' \n': "gg";
39 | }
40 |
41 | function codeWord(word) {
42 | return rfcMode ? "" + word + " " : "" + word + "";
43 | }
44 |
45 | function emphasize(word) {
46 | return rfcMode ? "" + word + " " : "" + word + " ";
47 | }
48 |
49 | function intGen(largeFlag, value) {
50 | let cborObject = CBOR.BigInt(value);
51 | let cbor = cborObject.encode();
52 | let size = " implicit";
53 | let type = "int";
54 | switch (cbor.length) {
55 | case 2:
56 | size = " one-byte";
57 | break;
58 | case 3:
59 | size = " two-byte";
60 | break;
61 | case 5:
62 | size = " four-byte";
63 | break;
64 | case 9:
65 | size = " eight-byte";
66 | break;
67 | case 11:
68 | size = "";
69 | type = "bigint";
70 | }
71 | let comment = (largeFlag ? "Largest" : "Smallest") + " " +
72 | (value < 0n ? "negative" : "positive") + size + " " + codeWord(type);
73 | oneTurn(cborObject.toString(), CBOR.toHex(cbor), comment);
74 | }
75 |
76 | let largest = false;
77 |
78 | function intGenBase(value) {
79 | intGen(largest, value);
80 | intGen(largest, -value - 1n);
81 | largest = !largest;
82 | }
83 |
84 | function floatGen(size, value, largest, subnormal) {
85 | let tag = 0xf9;
86 | let i = size;
87 | while (!(i & 2)) {
88 | i >>= 1;
89 | tag++;
90 | }
91 | let encoding = [];
92 | for (i = 0; i < size; i++) {
93 | encoding.push(Number(value & 255n));
94 | value >>= 8n;
95 | }
96 | encoding.push(tag);
97 | let cbor = CBOR.decode(new Uint8Array(encoding.reverse()));
98 | // console.log(cbor.toString());
99 | oneTurn(cbor.toString(), CBOR.toHex(encoding), (largest ? "Largest" : "Smallest") +
100 | " positive " + (subnormal ? 'subnormal ' : "") +
101 | codeWord('float' + (size * 8)));
102 | }
103 |
104 | function floatGenBase(size, exponentSize, offset) {
105 | floatGen(size, 1n, false, true);
106 | floatGen(size, offset - 1n, true, true);
107 | floatGen(size, offset, false, false);
108 | let exponent = 0n;
109 | for (let i = 1; i < exponentSize; i++) {
110 | exponent |= 1n;
111 | exponent <<= 1n;
112 | }
113 | floatGen(size, (exponent * offset) + offset - 1n, true, false);
114 | }
115 |
116 | if (intMode) {
117 | intGenBase(0n);
118 | intGenBase(23n);
119 | intGenBase(24n);
120 | let value = 256n;
121 | for (let i = 0; i < 4; i++) {
122 | intGenBase(value - 1n);
123 | intGenBase(value);
124 | value *= value;
125 | }
126 | } else {
127 | oneTurn("0.0", "f90000", "Zero");
128 |
129 | oneTurn("-0.0", "f98000", "Negative zero");
130 |
131 | oneTurn("Infinity", "f97c00", "Infinity");
132 |
133 | oneTurn("-Infinity", "f9fc00", "Negative infinity");
134 |
135 | oneTurn("NaN", "f97e00", "Not a number");
136 |
137 | floatGenBase(2, 5, 0x400n);
138 | floatGenBase(4, 8, 0x800000n);
139 | floatGenBase(8, 11, 0x10000000000000n);
140 | let text = ' -"-';
141 | oneTurn("-0.0000033333333333333333", "fbbecbf647612f3696", "Randomly selected number");
142 |
143 | oneTurn("10.559998512268066", "fa4128f5c1", text);
144 |
145 | oneTurn("10.559998512268068", "fb40251eb820000001", "Next in succession");
146 |
147 | oneTurn("295147905179352830000.0", "fa61800000", codeWord("268 ") +
148 | " (diagnostic notation truncates precision)");
149 |
150 | oneTurn("2.0", "f94000", "Number without a fractional part");
151 |
152 | oneTurn("-5.960464477539063e-8", "f98001",
153 | "Smallest negative subnormal " + codeWord("float16"));
154 |
155 | oneTurn("-5.960464477539062e-8", "fbbe6fffffffffffff",
156 | "Adjacent smallest negative subnormal " + codeWord("float16"));
157 |
158 | oneTurn("-5.960464477539064e-8", "fbbe70000000000001", text);
159 |
160 | oneTurn("-5.960465188081798e-8", "fab3800001", text);
161 |
162 | oneTurn("0.0000609755516052246", "fb3f0ff7ffffffffff",
163 | "Adjacent largest subnormal " + codeWord("float16"));
164 |
165 | oneTurn("0.000060975551605224616", "fb3f0ff80000000001", text);
166 |
167 | oneTurn("0.000060975555243203416", "fa387fc001", text);
168 |
169 | oneTurn("0.00006103515624999999", "fb3f0fffffffffffff",
170 | "Adjacent smallest " + codeWord("float16"));
171 |
172 | oneTurn("0.00006103515625000001", "fb3f10000000000001", text);
173 |
174 | oneTurn("0.00006103516352595761", "fa38800001", text);
175 |
176 | oneTurn("65503.99999999999", "fb40effbffffffffff",
177 | "Adjacent largest " + codeWord("float16"));
178 |
179 | oneTurn("65504.00000000001", "fb40effc0000000001", text);
180 |
181 | oneTurn("65504.00390625", "fa477fe001", text);
182 |
183 | oneTurn("1.4012984643248169e-45", "fb369fffffffffffff",
184 | "Adjacent smallest subnormal " + codeWord("float32"));
185 |
186 | oneTurn("1.4012984643248174e-45", "fb36a0000000000001", text);
187 |
188 | oneTurn("1.175494210692441e-38", "fb380fffffbfffffff",
189 | "Adjacent largest subnormal " + codeWord("float32"));
190 |
191 | oneTurn("1.1754942106924412e-38", "fb380fffffc0000001", text);
192 |
193 | oneTurn("1.1754943508222874e-38", "fb380fffffffffffff",
194 | "Adjacent smallest " + codeWord("float32"));
195 |
196 | oneTurn("1.1754943508222878e-38", "fb3810000000000001", text);
197 |
198 | oneTurn("3.4028234663852882e+38", "fb47efffffdfffffff",
199 | "Adjacent largest " + codeWord("float32"));
200 |
201 | oneTurn("3.402823466385289e+38", "fb47efffffe0000001", text);
202 |
203 | }
204 |
205 | table += rfcMode ? '\n
\n' : "hh";
206 | console.log("\n" + table + "\n");
207 |
--------------------------------------------------------------------------------
/doc/playground.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CBOR Playground
7 |
8 |
9 |
156 |
157 |
158 |
159 |
160 | CBOR Playground
161 |
162 |
CBOR.js
163 | supports the
164 | [
CBOR::Core ] primitives
166 | (
tstr,
bstr,
int,
167 |
bigint,
float,
168 |
bool,
null,
169 |
tagged data , and
170 |
simple values ).
171 |
172 |
173 |
174 |
Paste a CBOR object in the text box or try with the default:
189 |
190 |
200 |
203 |
204 |
205 |
206 |
--------------------------------------------------------------------------------
/doc/non-finite-numbers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Non-Finite Numbers
8 |
9 |
10 |
11 |
12 | Non-Finite Numbers
13 | Unlike JSON [RFC8259 ],
15 | CBOR supports the non-finite floating-point constructs defined by IEEE 754 , known as
16 | NaN (not a number) and Infinity.
17 | This implementation provides multiple levels of support, intended to
18 | accommodate different needs.
19 | This is accomplished without specific decoder or encoder options;
20 | it is entirely based on API method selections.
21 | The following table outlines the supported levels:
22 |
23 | Level Comment
24 | BASICOnly "regular" floating-point numbers are accepted.
25 |
26 | EXTENDEDAll floating-point numbers except for NaN with payloads are accepted.
27 | In the list below, NaN refers to "simple" NaNs
28 | (in deterministically encoded CBOR represented by f97e00),
29 | while Infinity refers to
30 | positive infinity (f97c00) and negative infinity (f9fc00).
31 | The JavaScript counterparts are represented by Number.NaN ,
32 | Number.POSITIVE_INFINITY ,
33 | and Number.NEGATIVE_INFINITY respectively.
34 |
35 | COMPLETEAll floating-point numbers are accepted.
36 | Note that the JavaScript runtime does not generate
37 | non-finite numbers beyond those supported by
EXTENDED.
38 | Also see:
Payload Option .
39 |
40 |
41 | Non-supported items cause a CborException to be thrown.
42 | Note that the constraints apply to encoding as well.
43 | Encoding Methods
44 |
57 | Decoding Methods
58 |
59 |
60 | BASIC
61 | cborObject.getFloat64() // "Regular" number
62 | EXTENDED
63 | cborObject.getExtendedFloat64() // "Regular" number, NaN, or Infinity
64 | COMPLETE
65 | // Check what the decoder returned:
66 | if (cborObject instanceof CBOR.NonFinite) {
67 | // Non-finite number to process
68 | // Select suitable method:
69 | cborObject.getNonFinite64() // Arbitrary non-finite number
70 | cborObject.getPayload() // Application-specific data
71 | } else {
72 | // "Regular" number to process
73 | cborObject.getFloat64() // "Regular" number
74 | }
75 |
76 |
77 | Payload Option
78 |
79 | Traditionally, the non-finite number space is used for propagating
80 | math-related problems such as division by zero.
81 |
82 |
83 | However, in some cases there may be a desire providing more application specific data,
84 | like information related to a faulty sensor that needs attention.
85 | The
CBOR.NonFinite.createPayload(payload)
86 | and
getPayload()
87 | methods were designed for this particular purpose.
88 | The following table represents these methods from a
developer perspective:
89 |
90 |
91 | Payload
92 | d51-d0 in big-endian order
93 |
94 |
95 | The payload bits are conceptually put into an IEEE 754
96 | 64-bit object having the following layout:
97 |
98 |
99 | Sign Exponent Significand
100 | 0 11111111111 d0-d51 in little-endian order
101 |
102 |
105 |
106 | The reason for reversing the payload bits is to ensure that a specific bit will remain
107 | in a fix position (maintain the same value), independent of the size of the
108 | IEEE 754 variant used for encoding.
109 |
110 |
111 | Note that the encoder will (due to CBOR deterministic encoding rules), select
112 | the shortest serialization required to properly represent the payload.
113 | The following table shows a few examples:
114 |
115 |
116 | Payload (hex) CBOR Encoding Diagnostic Notation
117 | 0f97c00Infinity
118 | 1f97e00NaN
119 | 2f97d00float'7d00'
120 | 3fff97ffffloat'7fff'
121 | 400fa7f801000float'7f801000'
122 | 7ffffffa7ffffffffloat'7fffffff'
123 | 800000fb7ff0000010000000float'7ff0000010000000'
124 | ffffffffffffffb7ffffffffffffffffloat'7fffffffffffffff'
125 |
126 | A payload of 0 and having the sign bit set, would encode as f9fc00 (-Infinity).
127 |
128 |
--------------------------------------------------------------------------------
/test/dates.js:
--------------------------------------------------------------------------------
1 | // Testing instant methods
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, success } from './assertions.js';
4 |
5 | function oneGetDateTime(epoch, isoString) {
6 | assertTrue("date1", CBOR.String(isoString).getDateTime().getTime() == epoch);
7 | let cbor = CBOR.decode(CBOR.String(isoString).encode());
8 | assertTrue("date2", cbor.getDateTime().getTime() == epoch);
9 | assertTrue("date3", CBOR.Tag(0n, CBOR.String(isoString)).getDateTime().getTime() == epoch);
10 | }
11 |
12 | function badDate(hexBor, err) {
13 | try {
14 | CBOR.decode(CBOR.fromHex(hexBor));
15 | fail("must not");
16 | } catch (error) {
17 | if (!error.toString().includes(err)) {
18 | throw error;
19 | }
20 | }
21 | }
22 |
23 | function truncateDateTime(iso, millis, seconds) {
24 | let dateTime = CBOR.String(iso).getDateTime();
25 | assertTrue("trdt1", dateTime.getTime() == millis);
26 | assertTrue("trdt2", CBOR.createDateTime(dateTime, true, false).getDateTime().getTime() == millis);
27 | assertTrue("trdt3", CBOR.createDateTime(dateTime, false, false).getDateTime().getTime() == seconds * 1000);
28 | }
29 |
30 | function truncateEpochTime(float, millis, seconds) {
31 | let epoch = CBOR.Float(float).getEpochTime();
32 | assertTrue("tr1", epoch.getTime() == millis);
33 | assertTrue("tr2", CBOR.createEpochTime(epoch, true).getEpochTime().getTime() == millis);
34 | assertTrue("tr3", CBOR.createEpochTime(epoch, false).getEpochTime().getTime() == seconds * 1000);
35 | }
36 |
37 | function oneGetEpochTime(hexBor, epoch, err) {
38 | let time = Math.floor((epoch * 1000) + 0.5);
39 | let instant = CBOR.decode(CBOR.fromHex(hexBor)).getEpochTime();
40 | assertTrue("epoch1", instant.getTime() == time);
41 | let cborObject = CBOR.createEpochTime(instant, time % 1000);
42 | // console.log("E=" + cborObject.toString());
43 | // console.log("1=" + cborObject.getEpochTime().getTime() + " 2=" + time);
44 | assertTrue("epoch2", cborObject.getEpochTime().getTime() == time);
45 | if (time % 1000 > 500) {
46 | let p1 = Math.floor(epoch + 1.0) * 1000;
47 | cborObject = CBOR.createEpochTime(instant, false);
48 | // console.log("r1=" + cborObject.getEpochTime().getTime() + " r2=" + p1);
49 | assertTrue("epoch3", cborObject.getEpochTime().getTime() == p1);
50 | }
51 | instant = CBOR.decode(CBOR.fromHex(hexBor));
52 | try {
53 | instant.checkForUnread();
54 | fail("must not");
55 | } catch (error) {
56 | if (!error.toString().includes(err)) {
57 | throw error;
58 | }
59 | }
60 | instant.getEpochTime();
61 | instant.checkForUnread();
62 | }
63 |
64 | oneGetDateTime(1740060548000, "2025-02-20T14:09:08+00:00");
65 | oneGetDateTime(1740060548000, "2025-02-20T14:09:08Z");
66 | oneGetDateTime(1740060548000, "2025-02-20T15:09:08+01:00");
67 | oneGetDateTime(1740060548000, "2025-02-20T15:39:08+01:30");
68 | oneGetDateTime(1740060548000, "2025-02-20T12:09:08-02:00");
69 | oneGetDateTime(1740060548000, "2025-02-20T11:39:08-02:30");
70 | oneGetDateTime(1740060548123, "2025-02-20T11:39:08.123-02:30");
71 | oneGetDateTime(1740060548930, "2025-02-20T14:09:08.930Z");
72 | // Next: Truncates!
73 | oneGetDateTime(1740060548930, "2025-02-20T14:09:08.9305Z");
74 | oneGetDateTime(-62167219200000, "0000-01-01T00:00:00Z");
75 | oneGetDateTime(253402300799000, "9999-12-31T23:59:59Z");
76 |
77 | badDate("c001", "got: CBOR.Int");
78 | badDate("c06135", "Invalid ISO date string: 5");
79 | badDate("c16135", "got: CBOR.String");
80 |
81 | oneGetEpochTime("1A67B73784", 1740060548, "Data of type=CBOR.Int");
82 | oneGetEpochTime("FB41D9EDCDE113645A", 1740060548.303, "Data of type=CBOR.Float with value=174");
83 | oneGetEpochTime("c1FB41D9EDCDE113645A", 1740060548.303, "Tagged object 1 of type=CBOR.Float");
84 | // Next: Truncates!
85 | // oneGetEpochTime("c1fb41d9edcde1136c8b", 1740060548.3035, "Tagged object 1 of type=CBOR.Float");
86 | // oneGetEpochTime("c1fb41d9edcde1204189", 1740060548.5045, "Tagged object 1 of type=CBOR.Float");
87 | oneGetEpochTime("c11b0000003afff4417f", 253402300799, "Tagged object 1 of type=CBOR.Int");
88 | oneGetEpochTime("00", 0, "Data of type=CBOR.Int");
89 |
90 | function oneMillis(time, iso) {
91 | let instant = new Date();
92 | instant.setTime(time);
93 | assertTrue("cdt1=", CBOR.createDateTime(instant, true, true).getString() == iso);
94 | let created = CBOR.createDateTime(instant, true, false);
95 | assertTrue("cdt2=", created.getDateTime().getTime() == time);
96 | assertTrue("cdt3=", created.getString().length == iso.length + 5);
97 | created = CBOR.createEpochTime(instant, true);
98 | assertTrue("cet1=", created.getEpochTime().getTime() == time);
99 | assertTrue("cet2=", created instanceof CBOR.Float == iso.includes("."));
100 | }
101 |
102 | oneMillis(1752189147000, "2025-07-10T23:12:27Z");
103 | oneMillis(1752189147123, "2025-07-10T23:12:27.123Z");
104 | oneMillis(1752189147120, "2025-07-10T23:12:27.12Z");
105 | oneMillis(1752189147100, "2025-07-10T23:12:27.1Z");
106 |
107 | truncateEpochTime(1740060548.000, 1740060548000, 1740060548);
108 | truncateEpochTime(0.0, 0, 0);
109 | truncateEpochTime(1740060548.0004, 1740060548000, 1740060548);
110 | truncateEpochTime(1740060548.0005, 1740060548001, 1740060548);
111 |
112 | truncateDateTime("2025-07-10T23:12:27Z", 1752189147000, 1752189147);
113 | truncateDateTime("2025-07-10T23:12:27.1Z", 1752189147100, 1752189147);
114 | truncateDateTime("2025-07-10T23:12:27.12Z", 1752189147120, 1752189147);
115 | truncateDateTime("2025-07-10T23:12:27.123Z", 1752189147123, 1752189147);
116 | truncateDateTime("2025-07-10T23:12:27.1233Z", 1752189147123, 1752189147);
117 | truncateDateTime("2025-07-10T23:12:27.1235Z", 1752189147123, 1752189147);
118 | truncateDateTime("2025-07-10T23:12:27.523Z", 1752189147523, 1752189148);
119 |
120 | truncateDateTime("1925-07-10T23:12:27Z", -1403570853000, -1403570853);
121 | truncateDateTime("1925-07-10T23:12:27.1Z", -1403570852900, -1403570853);
122 | truncateDateTime("1925-07-10T23:12:27.12Z", -1403570852880, -1403570853);
123 | truncateDateTime("1925-07-10T23:12:27.123Z", -1403570852877, -1403570853);
124 | truncateDateTime("1925-07-10T23:12:27.1233Z", -1403570852877, -1403570853);
125 | truncateDateTime("1925-07-10T23:12:27.1235Z", -1403570852877, -1403570853);
126 | truncateDateTime("1925-07-10T23:12:27.499Z", -1403570852501, -1403570853);
127 | truncateDateTime("1925-07-10T23:12:27.500Z", -1403570852500, -1403570852);
128 | truncateDateTime("1925-07-10T23:12:27.700Z", -1403570852300, -1403570852);
129 |
130 | try {
131 | // Z or -+local offset needed.
132 | CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43"));
133 | throw Error("Should not");
134 | } catch (error) {
135 | if (!error.toString().includes("ISO")) {
136 | throw error;
137 | }
138 | }
139 |
140 | try {
141 | // Beyond nano-seconds
142 | CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43.6666666666Z"));
143 | throw Error("Should not");
144 | } catch (error) {
145 | if (!error.toString().includes("ISO")) {
146 | throw error;
147 | }
148 | }
149 |
150 | try {
151 | // 24 hour is incorrect.
152 | CBOR.Tag(0n, CBOR.String("2023-06-22T24:01:43Z"));
153 | throw Error("Should not");
154 | } catch (error) {
155 | if (!error.toString().includes("ISO")) {
156 | throw error;
157 | }
158 | }
159 |
160 | [-1, 253402300800].forEach(epoch => {
161 | try {
162 | // Out of range for Date().
163 | CBOR.Tag(1n, CBOR.Int(epoch));
164 | throw Error("Should not");
165 | } catch (error) {
166 | if (!error.toString().includes("Epoch out of")) {
167 | throw error;
168 | }
169 | }
170 | try {
171 | // Out of range for Date().
172 | let instant = new Date();
173 | instant.setTime(epoch * 1000);
174 | CBOR.createEpochTime(instant, true);
175 | throw Error("Should not");
176 | } catch (error) {
177 | if (!error.toString().includes("Epoch out of")) {
178 | throw error;
179 | }
180 | }
181 | });
182 |
183 | assertTrue("zero", CBOR.String("0000-01-01T00:00:00Z").getDateTime().getTime() == -62167219200000);
184 |
185 | let now = new Date();
186 | /*
187 | console.log("Now=" + now.getTime() + " iso=" + now.toISOString() +
188 | " offset=" + now.getTimezoneOffset() + " str=" + now.toString());
189 | */
190 |
191 | function oneCreateDateTime(dateOrTime, utc, millis, bad) {
192 | let instant = new Date();
193 | let time = typeof dateOrTime == 'number' ? Math.round(dateOrTime) : dateOrTime.getTime();
194 | instant.setTime(time);
195 | if (bad) {
196 | try {
197 | CBOR.createDateTime(instant, millis, utc);
198 | throw Error("Should not");
199 | } catch (error) {
200 | if (!error.toString().includes("Date object out of range")) {
201 | throw error;
202 | }
203 | }
204 | } else {
205 | let dateTime = CBOR.createDateTime(instant, millis, utc);
206 | if (millis || !(time % 1000)) {
207 | assertTrue("cdt1" + dateTime, dateTime.getDateTime().getTime() == time);
208 | } else if (!millis && time % 1000) {
209 | assertFalse("cdt2" + dateTime, dateTime.getDateTime().getTime() == time);
210 | }
211 | }
212 | }
213 |
214 | oneCreateDateTime(now, true, false);
215 | oneCreateDateTime(now, true, true);
216 | oneCreateDateTime(now, false, false);
217 | oneCreateDateTime(now, false, true);
218 |
219 | oneCreateDateTime(1740060548000, true, false);
220 | oneCreateDateTime(1740060548000, true, true);
221 | oneCreateDateTime(1740060548501, true, false);
222 | oneCreateDateTime(1740060548501, true, true);
223 | oneCreateDateTime(1740060548000.3, true, true);
224 | oneCreateDateTime(1740060548000.5, true, true);
225 | oneCreateDateTime(-62167219200000, true, true);
226 | oneCreateDateTime(-62167219200001, true, true, true);
227 | oneCreateDateTime(253402300799000, true, true);
228 | oneCreateDateTime(253402300799001, true, true, true);
229 |
230 | success();
--------------------------------------------------------------------------------
/test/float.js:
--------------------------------------------------------------------------------
1 | // Test program for floating-point "edge cases"
2 | import CBOR from '../npm/mjs/index.mjs';
3 | import { assertTrue, assertFalse, fail, success } from './assertions.js';
4 |
5 | function overflow(decodedValue, length) {
6 | let test = 'decodedValue.getFloat' + length + '()';
7 | try {
8 | eval(test);
9 | assertTrue("Should fail", false);
10 | } catch (error) {
11 | if (!error.toString().includes('Value out of range:')) {
12 | throw error;
13 | }
14 | }
15 | }
16 |
17 | function shouldpass(decodedValue, value, length, valueText) {
18 | assertTrue("p1", decodedValue.toString() == valueText);
19 | let test = 'decodedValue.getFloat' + length + '()';
20 | let float = eval(test);
21 | assertTrue("p2", float == value);
22 | if (length == "64") {
23 | test = 'decodedValue.getExtendedFloat' + length + '()';
24 | float = eval(test);
25 | assertTrue("p3", float == value);
26 | }
27 | }
28 |
29 | function oneTurn(valueText, expected) {
30 | let value = Number(valueText);
31 | if (Number.isFinite(value)) {
32 | try {
33 | CBOR.NonFinite(value);
34 | fail("f1");
35 | } catch (error) {
36 | assertTrue("f2", error.toString().includes("bigint"));
37 | }
38 | let cbor = CBOR.Float(value).encode();
39 | assertTrue("f3", CBOR.toHex(cbor) == expected);
40 | let decodedValue = CBOR.decode(cbor);
41 | switch (cbor.length) {
42 | case 3:
43 | shouldpass(decodedValue, value, "16", valueText);
44 | shouldpass(decodedValue, value, "32", valueText);
45 | shouldpass(decodedValue, value, "64", valueText);
46 | break;
47 |
48 | case 5:
49 | shouldpass(decodedValue, value, "32", valueText);
50 | shouldpass(decodedValue, value, "64", valueText);
51 | overflow(decodedValue, "16");
52 | break;
53 |
54 | case 9:
55 | shouldpass(decodedValue, value, "64", valueText);
56 | overflow(decodedValue, "16");
57 | overflow(decodedValue, "32");
58 | break;
59 |
60 | default:
61 | fail("No such length");
62 | }
63 | } else {
64 | try {
65 | CBOR.Float(value);
66 | fail('Should not execute');
67 | } catch (error) {
68 | assertTrue("nf1", error.toString().includes('CBOR.NonFinite'));
69 | }
70 | let decodedValue = CBOR.Float.createExtendedFloat(value);
71 | assertTrue("nf2", decodedValue.getExtendedFloat64().toString() == value.toString());
72 | assertTrue("nf3", decodedValue.toString() == value.toString());
73 | let cbor = decodedValue.encode();
74 | assertTrue("nf4", CBOR.toHex(cbor) == expected);
75 | assertTrue("nf5", CBOR.decode(cbor).equals(decodedValue));
76 | let buf = new Uint8Array(8);
77 | new DataView(buf.buffer, 0, 8).setFloat64(0, value, false);
78 | assertTrue("nf6", decodedValue.getNonFinite64() == CBOR.toBigInt(buf));
79 | }
80 | assertTrue("d10", CBOR.toHex(CBOR.Float.createExtendedFloat(value).encode()) == expected);
81 | }
82 |
83 | const inNanWithPayload = new Uint8Array([0x7f, 0xf8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]);
84 |
85 | let value = new DataView(inNanWithPayload.buffer, 0, 8).getFloat64(0, false);
86 |
87 | let outNanWithPayload = new Uint8Array(8);
88 | new DataView(outNanWithPayload.buffer, 0, 8).setFloat64(0, value, false);
89 |
90 | let supportNanWithPayloads = true;
91 | for (let q = 0; q < 8; q++) {
92 | if (inNanWithPayload[q] != outNanWithPayload[q]) {
93 | // console.log(outNanWithPayload.toString());
94 | console.log('This implementation does not support NaN with payloads');
95 | supportNanWithPayloads = false;
96 | break;
97 | }
98 | }
99 |
100 | function payloadOneTurn(payload, hex, dn) {
101 | dn = dn == null ? "float'" + hex.substring(2) + "'" : dn;
102 | let cbor = CBOR.NonFinite.createPayload(payload).encode();
103 | let object = CBOR.decode(cbor);
104 | assertTrue("plo1", object instanceof CBOR.NonFinite);
105 | let nonFinite = object;
106 | assertTrue("plo2", nonFinite.getPayload() == payload);
107 | assertTrue("plo3", CBOR.toHex(cbor) == hex);
108 | assertTrue("plo4", nonFinite.toString() == dn);
109 | assertTrue("plo5", nonFinite.getNonFinite() == CBOR.toBigInt(CBOR.fromHex(hex.substring(2), 16)));
110 | assertFalse("plo6", nonFinite.getSign());
111 | let signedHex = hex.substring(0, 2) + "f" +hex.substring(3);
112 | nonFinite.setSign(true);
113 | assertTrue("plo7", nonFinite.getSign());
114 | assertTrue("plo8", CBOR.toHex(nonFinite.encode()) == signedHex);
115 | nonFinite = CBOR.NonFinite.createPayload(payload).setSign(true);
116 | assertTrue("plo9", CBOR.toHex(nonFinite.encode()) == signedHex);
117 | }
118 |
119 | function oneNonFiniteTurn(value, binexpect, textexpect) {
120 | let nonfinite = CBOR.NonFinite(value);
121 | let text = nonfinite.toString();
122 | let returnValue = nonfinite.getNonFinite();
123 | let returnValue64 = nonfinite.getNonFinite64();
124 | let textdecode = CBOR.diagDecode(textexpect);
125 | let cbor = nonfinite.encode();
126 | let refcbor = CBOR.fromHex(binexpect);
127 | let hexbin = CBOR.toHex(cbor);
128 | assertTrue("eq1", text == textexpect);
129 | assertTrue("eq2", hexbin == binexpect);
130 | assertTrue("eq3", returnValue == CBOR.decode(cbor).getNonFinite());
131 | assertTrue("eq4", returnValue == textdecode.getNonFinite());
132 | assertTrue("eq5", CBOR.fromBigInt(returnValue).length == nonfinite.length);
133 | assertTrue("eq7", CBOR.fromBigInt(returnValue64).length == 8);
134 | assertTrue("eq8", nonfinite.equals(CBOR.decode(cbor)));
135 | let rawcbor = CBOR.fromBigInt(value);
136 | rawcbor = CBOR.addArrays(new Uint8Array([0xf9 + (rawcbor.length >> 2)]), rawcbor);
137 | if (rawcbor.length > refcbor.length) {
138 | try {
139 | CBOR.decode(rawcbor);
140 | fail("d1");
141 | } catch(error) {
142 | assertTrue("d2", error.toString().includes("Non-deterministic"));
143 | }
144 | } else {
145 | CBOR.decode(rawcbor);
146 | }
147 | assertTrue("d3", CBOR.initDecoder(rawcbor, CBOR.LENIENT_NUMBER_DECODING)
148 | .decodeWithOptions().equals(nonfinite));
149 | let object = CBOR.decode(refcbor);
150 | if (textexpect.includes("NaN") || textexpect.includes("Infinity")) {
151 | assertTrue("d4", object.getExtendedFloat64().toString() == textexpect);
152 | assertTrue("d5", object.isSimple());
153 | assertTrue("d6", textexpect.includes("Infinity") ^ object.isNaN());
154 | } else {
155 | try {
156 | object.getExtendedFloat64();
157 | fail("d7");
158 | } catch (error) {
159 | assertTrue("d8", error.toString().includes("7e00"));
160 | }
161 | assertFalse("d9", object.isSimple());
162 | }
163 | }
164 |
165 | oneTurn("0.0", "f90000");
166 | oneTurn("-0.0", "f98000");
167 | oneTurn("NaN", "f97e00");
168 | oneTurn("Infinity", "f97c00");
169 | oneTurn("-Infinity", "f9fc00");
170 | oneTurn("0.0000610649585723877", "fa38801000");
171 | oneTurn("10.559998512268066", "fa4128f5c1");
172 | oneTurn("65472.0", "f97bfe");
173 | oneTurn("65472.00390625", "fa477fc001");
174 | oneTurn("65503.0", "fa477fdf00");
175 | oneTurn("65504.0", "f97bff");
176 | oneTurn("65504.00000000001", "fb40effc0000000001");
177 | oneTurn("65504.00390625", "fa477fe001");
178 | oneTurn("65504.5", "fa477fe080");
179 | oneTurn("65505.0", "fa477fe100");
180 | oneTurn("131008.0", "fa47ffe000");
181 | oneTurn("-5.960464477539062e-8", "fbbe6fffffffffffff");
182 | oneTurn("-5.960464477539063e-8", "f98001");
183 | oneTurn("-5.960464477539064e-8", "fbbe70000000000001");
184 | oneTurn("-5.960465188081798e-8", "fab3800001");
185 | oneTurn("-5.963374860584736e-8", "fab3801000");
186 | oneTurn("-5.966285243630409e-8", "fab3802000");
187 | oneTurn("-8.940696716308594e-8", "fab3c00000");
188 | oneTurn("-0.00006097555160522461", "f983ff");
189 | oneTurn("-0.000060975551605224616", "fbbf0ff80000000001");
190 | oneTurn("-0.000060975555243203416", "fab87fc001");
191 | oneTurn("0.00006103515625", "f90400");
192 | oneTurn("0.00006103515625005551", "fb3f10000000001000");
193 | oneTurn("1.4012984643248169e-45", "fb369fffffffffffff");
194 | oneTurn("1.401298464324817e-45", "fa00000001");
195 | oneTurn("1.4012984643248174e-45", "fb36a0000000000001");
196 | oneTurn("1.4012986313726115e-45", "fb36a0000020000000");
197 | oneTurn("1.1754942106924411e-38", "fa007fffff");
198 | oneTurn("3.4028234663852886e+38", "fa7f7fffff");
199 | oneTurn("3.402823466385289e+38", "fb47efffffe0000001");
200 | oneTurn("0.00006109476089477539", "f90401");
201 | oneTurn("7.52316384526264e-37", "fa03800000");
202 | oneTurn("1.1754943508222875e-38", "fa00800000");
203 | oneTurn("5.0e-324", "fb0000000000000001");
204 | oneTurn("-1.7976931348623157e+308", "fbffefffffffffffff");
205 |
206 | oneNonFiniteTurn(0x7e00n, "f97e00", "NaN");
207 | oneNonFiniteTurn(0x7c01n, "f97c01", "float'7c01'");
208 | oneNonFiniteTurn(0xfc01n, "f9fc01", "float'fc01'");
209 | oneNonFiniteTurn(0x7fffn, "f97fff", "float'7fff'");
210 | oneNonFiniteTurn(0xfe00n, "f9fe00", "float'fe00'");
211 | oneNonFiniteTurn(0x7c00n, "f97c00", "Infinity");
212 | oneNonFiniteTurn(0xfc00n, "f9fc00", "-Infinity");
213 |
214 | oneNonFiniteTurn(0x7fc00000n, "f97e00", "NaN");
215 | oneNonFiniteTurn(0x7f800001n, "fa7f800001", "float'7f800001'");
216 | oneNonFiniteTurn(0xff800001n, "faff800001", "float'ff800001'");
217 | oneNonFiniteTurn(0x7fffffffn, "fa7fffffff", "float'7fffffff'");
218 | oneNonFiniteTurn(0xffc00000n, "f9fe00", "float'fe00'");
219 | oneNonFiniteTurn(0x7f800000n, "f97c00", "Infinity");
220 | oneNonFiniteTurn(0xff800000n, "f9fc00", "-Infinity");
221 |
222 | oneNonFiniteTurn(0x7ff8000000000000n, "f97e00", "NaN");
223 | oneNonFiniteTurn(0x7ff0000000000001n, "fb7ff0000000000001", "float'7ff0000000000001'");
224 | oneNonFiniteTurn(0xfff0000000000001n, "fbfff0000000000001", "float'fff0000000000001'");
225 | oneNonFiniteTurn(0x7fffffffffffffffn, "fb7fffffffffffffff", "float'7fffffffffffffff'");
226 | oneNonFiniteTurn(0x7ff0000020000000n, "fa7f800001", "float'7f800001'");
227 | oneNonFiniteTurn(0xfff0000020000000n, "faff800001", "float'ff800001'");
228 | oneNonFiniteTurn(0xfff8000000000000n, "f9fe00", "float'fe00'");
229 | oneNonFiniteTurn(0x7ff0040000000000n, "f97c01", "float'7c01'");
230 | oneNonFiniteTurn(0x7ff0000000000000n, "f97c00", "Infinity");
231 | oneNonFiniteTurn(0xfff0000000000000n, "f9fc00", "-Infinity");
232 |
233 | // Very special, some platforms natively support NaN with payloads, but we don't care
234 | // "signaling" NaN
235 | try {
236 | let nanWithPayload = new Uint8Array([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
237 | CBOR.Float.createExtendedFloat(new DataView(nanWithPayload.buffer, 0, 8).getFloat64(0, false));
238 | assertFalse("must not", supportNanWithPayloads);
239 | } catch (error) {
240 | assertTrue("not", error.toString().includes("payloads"));
241 | }
242 | let nonFinite = CBOR.Float.createExtendedFloat(Number.NaN);
243 | assertTrue("conv", nonFinite instanceof CBOR.NonFinite);
244 | assertTrue("truncated", nonFinite.getNonFinite64() == 0x7ff8000000000000n); // Returns "quiet" NaN
245 | assertTrue("cbor", CBOR.toHex(nonFinite.encode()) == "f97e00"); // Encoded as it should
246 | assertTrue("combined", Number.isNaN(nonFinite.getExtendedFloat64())); // Returns "Number"
247 | assertTrue("nan", nonFinite.isNaN(false)); // Indeed it is
248 |
249 | payloadOneTurn(0n, "f97c00", "Infinity");
250 | payloadOneTurn(1n, "f97e00", "NaN");
251 | payloadOneTurn(2n, "f97d00", null);
252 | payloadOneTurn((1n << 10n) - 1n, "f97fff", null);
253 | payloadOneTurn(1n << 10n, "fa7f801000", null);
254 | payloadOneTurn((1n << 23n) - 1n, "fa7fffffff", null);
255 | payloadOneTurn(1n << 23n, "fb7ff0000010000000", null);
256 | payloadOneTurn((1n << 52n) - 1n, "fb7fffffffffffffff", null);
257 |
258 | try {
259 | CBOR.NonFinite.createPayload(1n << 52n).encode();
260 | fail("pl8");
261 | } catch(error) {
262 | assertTrue("p18a", error.toString().includes("Payload out of range"));
263 | }
264 |
265 | success();
266 |
--------------------------------------------------------------------------------
/npm/test-all.mjs:
--------------------------------------------------------------------------------
1 | // Testing CBOR.js API
2 | import CBOR from 'cbor-object';
3 |
4 | let failures = 0;
5 | let test = 0;
6 | let name = '';
7 |
8 | function assertTrue(text, bool) {
9 | if (!bool) throw Error("Assertion: " + text);
10 | }
11 |
12 | function assertFalse(text, bool) {
13 | if (bool) throw Error("Assertion: " + text);
14 | }
15 |
16 | function success() {
17 | console.log('Test ' + name + ' was successful');
18 | }
19 |
20 | let TESTS=[
21 |
22 | {name:'base64.js',
23 | file:String.raw`// Testing the B64U/B64 converters
24 |
25 | let bin = new Uint8Array(256);
26 | for (let i = 0; i < bin.length; i++) {
27 | bin[i] = i;
28 | }
29 | let b64U = CBOR.toBase64Url(bin);
30 | assertFalse("cmp1", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64U)));
31 |
32 | // This is what "btoa" returns for bin:
33 | let b64 = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissL\
34 | S4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY\
35 | 2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYm\
36 | ZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz\
37 | 9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==';
38 |
39 | // fromBase64Url is "permissive" and takes Base64 with padding as well...
40 | assertFalse("cmp2", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64)));
41 |
42 | assertFalse("cmp3", CBOR.compareArrays(CBOR.fromBase64Url('oQVkZGF0YQ'),
43 | CBOR.fromHex('a1056464617461')));
44 | // Zero data is compliant
45 | assertFalse("cmp4", CBOR.compareArrays(CBOR.fromBase64Url(''), new Uint8Array()));
46 | assertTrue("cmp4", CBOR.toBase64Url(new Uint8Array()) == "");
47 | success();
48 | `}
49 | ,
50 | {name:'check-for-unread.js',
51 | file:String.raw`// Testing the "checkForUnread()" feature
52 |
53 | function oneTurn(create, access, errorString) {
54 | let res = eval(create);
55 | try {
56 | res.checkForUnread();
57 | if (errorString !== null) {
58 | throw Error("no way");
59 | }
60 | } catch (error) {
61 | if (!error.toString().includes('never read')) {
62 | throw error;
63 | }
64 | }
65 | try {
66 | eval(access);
67 | res.checkForUnread();
68 | assertFalse("cfu1", errorString);
69 | } catch (error) {
70 | assertTrue("cfu2=" + error, errorString);
71 | if (!error.toString().includes(errorString)) {
72 | throw error;
73 | }
74 | }
75 | eval(create).scan().checkForUnread();
76 | }
77 |
78 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
79 | "res.get(0).get(CBOR.Int(1)).getString()");
80 |
81 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
82 | "res",
83 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
84 |
85 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
86 | "res.get(0).get(CBOR.Int(1))",
87 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
88 |
89 | oneTurn("CBOR.Array().add(CBOR.Map())",
90 | "res",
91 | "Array element of type=CBOR.Map with value={} was never read");
92 |
93 | // Empty Map => nothing to read
94 | oneTurn("CBOR.Array().add(CBOR.Map())",
95 | "res.get(0)",
96 | "Array element of type=CBOR.Map with value={} was never read");
97 |
98 | oneTurn("CBOR.Array().add(CBOR.Map())",
99 | "res.get(0).scan()",
100 | null);
101 |
102 | // Empty Array => nothing to read
103 | oneTurn("CBOR.Array()",
104 | "res",
105 | "Data of type=CBOR.Array with value=[] was never read");
106 |
107 | oneTurn("CBOR.Array()",
108 | "res.scan()",
109 | null);
110 |
111 | oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
112 | "res.get().get(CBOR.Int(1)).getString()");
113 |
114 | oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
115 | "res.get()",
116 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
117 |
118 | oneTurn("CBOR.Tag(8n, CBOR.Map())",
119 | "res.get()",
120 | "Tagged object 8 of type=CBOR.Map with value={} was never read");
121 |
122 | oneTurn("CBOR.Simple(8)",
123 | "res",
124 | "Data of type=CBOR.Simple with value=simple(8) was never read");
125 |
126 | oneTurn("CBOR.Simple(8)",
127 | "res.getSimple()",
128 | null);
129 |
130 | oneTurn("CBOR.Tag(8n, CBOR.Map())",
131 | "res.get().scan()",
132 | null);
133 |
134 | // Date time specials
135 | oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
136 | "res.get()",
137 | "Tagged object 0 of type=CBOR.String with value=\"2025-02-20T14:09:08Z\" was never read");
138 |
139 | oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
140 | "res.get().getString()",
141 | null);
142 |
143 | oneTurn("CBOR.Tag(8n, CBOR.Int(2))",
144 | "res.get()",
145 | "Tagged object 8 of type=CBOR.Int with value=2 was never read");
146 |
147 | oneTurn("CBOR.Int(1)",
148 | "res.getInt32()");
149 | success();
150 | `}
151 | ,
152 | {name:'clone.js',
153 | file:String.raw`// Testing the "clone()" and "equals() methods
154 |
155 | let object = CBOR.Map()
156 | .set(CBOR.Int(2), CBOR.Array()
157 | .add(CBOR.Boolean(false)));
158 | assertTrue("clone+equals", object.equals(object.clone()));
159 | let copy = object.clone().set(CBOR.Int(1), CBOR.String("Hi"));
160 | assertFalse("copy+equals+clone", copy.equals(object));
161 |
162 | success();
163 | `}
164 | ,
165 | {name:'cotx.js',
166 | file:String.raw`// Testing the COTX identifier
167 |
168 | function oneTurn(hex, dn, ok) {
169 | try {
170 | let object = CBOR.decode(CBOR.fromHex(hex));
171 | assertTrue("Should not execute", ok);
172 | if (object.toString() != dn.toString() || !object.equals(CBOR.decode(object.encode()))) {
173 | throw Error("non match:" + dn + " " + object.toString());
174 | }
175 | } catch (error) {
176 | if (ok) console.log(error.toString());
177 | assertFalse("Must succeed", ok);
178 | }
179 | }
180 |
181 | oneTurn('d903f2623737', '1010("77")', false);
182 | oneTurn('d903f281623737', '1010(["77"])', false);
183 | oneTurn('d903f28206623737', '1010([6, "77"])', false);
184 | oneTurn('d903f28262373707', '1010(["77", 7])', true);
185 |
186 | success();
187 | `}
188 | ,
189 | {name:'diagnostic.js',
190 | file:String.raw`// Testing "diagnostic notation"
191 |
192 | function oneTurn(cborText, ok, compareWithOrNull) {
193 | try {
194 | let compareText = compareWithOrNull ? compareWithOrNull : cborText;
195 | let result = CBOR.diagDecode(cborText);
196 | assertTrue("Should not", ok);
197 | let sequence = CBOR.diagDecodeSequence(cborText);
198 | if (result.toString() != compareText) {
199 | throw Error("input:\n" + cborText + "\nresult:\n" + result);
200 | }
201 | assertTrue("seq", sequence.length == 1);
202 | if (sequence[0].toString() != compareText) {
203 | throw Error("input:\n" + cborText + "\nresult:\n" + result);
204 | }
205 | } catch (error) {
206 | assertFalse("Err: " + error, ok);
207 | }
208 | }
209 |
210 | function oneBinaryTurn(diag, hex) {
211 | assertTrue("bin", CBOR.toHex(CBOR.diagDecode(diag).encode()) == hex);
212 | }
213 |
214 | oneTurn("2", true, null);
215 | oneTurn("2.0", true, null);
216 | oneTurn("123456789012345678901234567890", true, null);
217 | oneTurn("Infinity", true, null);
218 | oneTurn("-Infinity", true, null);
219 | oneTurn("NaN", true, null);
220 | oneTurn("0.0", true, null);
221 | oneTurn("-0.0", true, null);
222 | oneTurn('{\n 4: "hi"\n}', true, null);
223 | oneTurn('[4, true, false, null]', true, null);
224 | oneTurn('"next\nline\r\\\ncont\r\nk"', true, '"next\\nline\\ncont\\nk"');
225 | oneTurn('{1:<< 5 , 7 >>}', true, "{\n 1: h'0507'\n}");
226 | oneTurn('<<[3.0]>>', true, "h'81f94200'");
227 | oneTurn('0b100_000000001', true, "2049");
228 | oneTurn('4.0e+500', false, null);
229 | oneTurn('4.0e+5', true, "400000.0");
230 | oneTurn('"missing', false, null);
231 | oneTurn('simple(21)', true, 'true');
232 | oneTurn('simple(59)', true, 'simple(59)');
233 | oneBinaryTurn('"\\ud800\\udd51"', "64f0908591");
234 | oneBinaryTurn("'\\u20ac'", "43e282ac");
235 | oneBinaryTurn('"\\"\\\\\\b\\f\\n\\r\\t"', "67225c080c0a0d09");
236 |
237 | let cborObject = CBOR.decode(CBOR.fromHex('a20169746578740a6e6578740284fa3380000147a10564646\
238 | 17461a1f5f4c074323032332d30362d30325430373a35333a31395a'));
239 |
240 | let cborText = '{\n' +
241 | ' 1: "text\\nnext",\n' +
242 | ' 2: [\n' +
243 | ' 5.960465188081798e-8,\n' +
244 | ' h\'a1056464617461\',\n' +
245 | ' {\n' +
246 | ' true: false\n' +
247 | ' },\n' +
248 | ' 0("2023-06-02T07:53:19Z")\n' +
249 | ' ]\n' +
250 | '}';
251 |
252 | assertTrue("pretty", cborObject.toDiag(true) == cborText);
253 | assertTrue("oneline", cborObject.toDiag(false) ==
254 | cborText.replaceAll('\n', '').replaceAll(' ',''));
255 | assertTrue("parse", CBOR.diagDecode(cborText).equals(cborObject));
256 | let sequence = CBOR.diagDecodeSequence('45,{4:7}');
257 | assertTrue("seq2", sequence.length == 2);
258 | assertTrue("seq3", sequence[0].getInt32() == 45);
259 | assertTrue("seq4", sequence[1].equals(CBOR.Map().set(CBOR.Int(4),CBOR.Int(7))));
260 |
261 | try {
262 | CBOR.diagDecode("float'000000'");
263 | fail("bugf");
264 | } catch (error) {
265 | assertTrue("fp", error.toString().includes('floating-point'));
266 | }
267 |
268 | success();
269 | `}
270 | ,
271 | {name:'float.js',
272 | file:String.raw`// Test program for floating-point "edge cases"
273 |
274 | function overflow(decodedValue, length) {
275 | let test = 'decodedValue.getFloat' + length + '()';
276 | try {
277 | eval(test);
278 | assertTrue("Should fail", false);
279 | } catch (error) {
280 | if (!error.toString().includes('Value out of range:')) {
281 | throw error;
282 | }
283 | }
284 | }
285 |
286 | function shouldpass(decodedValue, value, length, valueText) {
287 | assertTrue("p1", decodedValue.toString() == valueText);
288 | let test = 'decodedValue.getFloat' + length + '()';
289 | let float = eval(test);
290 | assertTrue("p2", float == value);
291 | if (length == "64") {
292 | test = 'decodedValue.getExtendedFloat' + length + '()';
293 | float = eval(test);
294 | assertTrue("p3", float == value);
295 | }
296 | }
297 |
298 | function oneTurn(valueText, expected) {
299 | let value = Number(valueText);
300 | if (Number.isFinite(value)) {
301 | try {
302 | CBOR.NonFinite(value);
303 | fail("f1");
304 | } catch (error) {
305 | assertTrue("f2", error.toString().includes("bigint"));
306 | }
307 | let cbor = CBOR.Float(value).encode();
308 | assertTrue("f3", CBOR.toHex(cbor) == expected);
309 | let decodedValue = CBOR.decode(cbor);
310 | switch (cbor.length) {
311 | case 3:
312 | shouldpass(decodedValue, value, "16", valueText);
313 | shouldpass(decodedValue, value, "32", valueText);
314 | shouldpass(decodedValue, value, "64", valueText);
315 | break;
316 |
317 | case 5:
318 | shouldpass(decodedValue, value, "32", valueText);
319 | shouldpass(decodedValue, value, "64", valueText);
320 | overflow(decodedValue, "16");
321 | break;
322 |
323 | case 9:
324 | shouldpass(decodedValue, value, "64", valueText);
325 | overflow(decodedValue, "16");
326 | overflow(decodedValue, "32");
327 | break;
328 |
329 | default:
330 | fail("No such length");
331 | }
332 | } else {
333 | try {
334 | CBOR.Float(value);
335 | fail('Should not execute');
336 | } catch (error) {
337 | assertTrue("nf1", error.toString().includes('CBOR.NonFinite'));
338 | }
339 | let decodedValue = CBOR.Float.createExtendedFloat(value);
340 | assertTrue("nf2", decodedValue.getExtendedFloat64().toString() == value.toString());
341 | assertTrue("nf3", decodedValue.toString() == value.toString());
342 | let cbor = decodedValue.encode();
343 | assertTrue("nf4", CBOR.toHex(cbor) == expected);
344 | assertTrue("nf5", CBOR.decode(cbor).equals(decodedValue));
345 | let buf = new Uint8Array(8);
346 | new DataView(buf.buffer, 0, 8).setFloat64(0, value, false);
347 | assertTrue("nf6", decodedValue.getNonFinite64() == CBOR.toBigInt(buf));
348 | }
349 | assertTrue("d10", CBOR.toHex(CBOR.Float.createExtendedFloat(value).encode()) == expected);
350 | }
351 |
352 | const inNanWithPayload = new Uint8Array([0x7f, 0xf8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]);
353 |
354 | let value = new DataView(inNanWithPayload.buffer, 0, 8).getFloat64(0, false);
355 |
356 | let outNanWithPayload = new Uint8Array(8);
357 | new DataView(outNanWithPayload.buffer, 0, 8).setFloat64(0, value, false);
358 |
359 | let supportNanWithPayloads = true;
360 | for (let q = 0; q < 8; q++) {
361 | if (inNanWithPayload[q] != outNanWithPayload[q]) {
362 | // console.log(outNanWithPayload.toString());
363 | console.log('This implementation does not support NaN with payloads');
364 | supportNanWithPayloads = false;
365 | break;
366 | }
367 | }
368 |
369 | function payloadOneTurn(payload, hex, dn) {
370 | dn = dn == null ? "float'" + hex.substring(2) + "'" : dn;
371 | let cbor = CBOR.NonFinite.createPayload(payload).encode();
372 | let object = CBOR.decode(cbor);
373 | assertTrue("plo1", object instanceof CBOR.NonFinite);
374 | let nonFinite = object;
375 | assertTrue("plo2", nonFinite.getPayload() == payload);
376 | assertTrue("plo3", CBOR.toHex(cbor) == hex);
377 | assertTrue("plo4", nonFinite.toString() == dn);
378 | assertTrue("plo5", nonFinite.getNonFinite() == CBOR.toBigInt(CBOR.fromHex(hex.substring(2), 16)));
379 | assertFalse("plo6", nonFinite.getSign());
380 | let signedHex = hex.substring(0, 2) + "f" +hex.substring(3);
381 | nonFinite.setSign(true);
382 | assertTrue("plo7", nonFinite.getSign());
383 | assertTrue("plo8", CBOR.toHex(nonFinite.encode()) == signedHex);
384 | nonFinite = CBOR.NonFinite.createPayload(payload).setSign(true);
385 | assertTrue("plo9", CBOR.toHex(nonFinite.encode()) == signedHex);
386 | }
387 |
388 | function oneNonFiniteTurn(value, binexpect, textexpect) {
389 | let nonfinite = CBOR.NonFinite(value);
390 | let text = nonfinite.toString();
391 | let returnValue = nonfinite.getNonFinite();
392 | let returnValue64 = nonfinite.getNonFinite64();
393 | let textdecode = CBOR.diagDecode(textexpect);
394 | let cbor = nonfinite.encode();
395 | let refcbor = CBOR.fromHex(binexpect);
396 | let hexbin = CBOR.toHex(cbor);
397 | assertTrue("eq1", text == textexpect);
398 | assertTrue("eq2", hexbin == binexpect);
399 | assertTrue("eq3", returnValue == CBOR.decode(cbor).getNonFinite());
400 | assertTrue("eq4", returnValue == textdecode.getNonFinite());
401 | assertTrue("eq5", CBOR.fromBigInt(returnValue).length == nonfinite.length);
402 | assertTrue("eq7", CBOR.fromBigInt(returnValue64).length == 8);
403 | assertTrue("eq8", nonfinite.equals(CBOR.decode(cbor)));
404 | let rawcbor = CBOR.fromBigInt(value);
405 | rawcbor = CBOR.addArrays(new Uint8Array([0xf9 + (rawcbor.length >> 2)]), rawcbor);
406 | if (rawcbor.length > refcbor.length) {
407 | try {
408 | CBOR.decode(rawcbor);
409 | fail("d1");
410 | } catch(error) {
411 | assertTrue("d2", error.toString().includes("Non-deterministic"));
412 | }
413 | } else {
414 | CBOR.decode(rawcbor);
415 | }
416 | assertTrue("d3", CBOR.initDecoder(rawcbor, CBOR.LENIENT_NUMBER_DECODING)
417 | .decodeWithOptions().equals(nonfinite));
418 | let object = CBOR.decode(refcbor);
419 | if (textexpect.includes("NaN") || textexpect.includes("Infinity")) {
420 | assertTrue("d4", object.getExtendedFloat64().toString() == textexpect);
421 | assertTrue("d5", object.isSimple());
422 | assertTrue("d6", textexpect.includes("Infinity") ^ object.isNaN());
423 | } else {
424 | try {
425 | object.getExtendedFloat64();
426 | fail("d7");
427 | } catch (error) {
428 | assertTrue("d8", error.toString().includes("7e00"));
429 | }
430 | assertFalse("d9", object.isSimple());
431 | }
432 | }
433 |
434 | oneTurn("0.0", "f90000");
435 | oneTurn("-0.0", "f98000");
436 | oneTurn("NaN", "f97e00");
437 | oneTurn("Infinity", "f97c00");
438 | oneTurn("-Infinity", "f9fc00");
439 | oneTurn("0.0000610649585723877", "fa38801000");
440 | oneTurn("10.559998512268066", "fa4128f5c1");
441 | oneTurn("65472.0", "f97bfe");
442 | oneTurn("65472.00390625", "fa477fc001");
443 | oneTurn("65503.0", "fa477fdf00");
444 | oneTurn("65504.0", "f97bff");
445 | oneTurn("65504.00000000001", "fb40effc0000000001");
446 | oneTurn("65504.00390625", "fa477fe001");
447 | oneTurn("65504.5", "fa477fe080");
448 | oneTurn("65505.0", "fa477fe100");
449 | oneTurn("131008.0", "fa47ffe000");
450 | oneTurn("-5.960464477539062e-8", "fbbe6fffffffffffff");
451 | oneTurn("-5.960464477539063e-8", "f98001");
452 | oneTurn("-5.960464477539064e-8", "fbbe70000000000001");
453 | oneTurn("-5.960465188081798e-8", "fab3800001");
454 | oneTurn("-5.963374860584736e-8", "fab3801000");
455 | oneTurn("-5.966285243630409e-8", "fab3802000");
456 | oneTurn("-8.940696716308594e-8", "fab3c00000");
457 | oneTurn("-0.00006097555160522461", "f983ff");
458 | oneTurn("-0.000060975551605224616", "fbbf0ff80000000001");
459 | oneTurn("-0.000060975555243203416", "fab87fc001");
460 | oneTurn("0.00006103515625", "f90400");
461 | oneTurn("0.00006103515625005551", "fb3f10000000001000");
462 | oneTurn("1.4012984643248169e-45", "fb369fffffffffffff");
463 | oneTurn("1.401298464324817e-45", "fa00000001");
464 | oneTurn("1.4012984643248174e-45", "fb36a0000000000001");
465 | oneTurn("1.4012986313726115e-45", "fb36a0000020000000");
466 | oneTurn("1.1754942106924411e-38", "fa007fffff");
467 | oneTurn("3.4028234663852886e+38", "fa7f7fffff");
468 | oneTurn("3.402823466385289e+38", "fb47efffffe0000001");
469 | oneTurn("0.00006109476089477539", "f90401");
470 | oneTurn("7.52316384526264e-37", "fa03800000");
471 | oneTurn("1.1754943508222875e-38", "fa00800000");
472 | oneTurn("5.0e-324", "fb0000000000000001");
473 | oneTurn("-1.7976931348623157e+308", "fbffefffffffffffff");
474 |
475 | oneNonFiniteTurn(0x7e00n, "f97e00", "NaN");
476 | oneNonFiniteTurn(0x7c01n, "f97c01", "float'7c01'");
477 | oneNonFiniteTurn(0xfc01n, "f9fc01", "float'fc01'");
478 | oneNonFiniteTurn(0x7fffn, "f97fff", "float'7fff'");
479 | oneNonFiniteTurn(0xfe00n, "f9fe00", "float'fe00'");
480 | oneNonFiniteTurn(0x7c00n, "f97c00", "Infinity");
481 | oneNonFiniteTurn(0xfc00n, "f9fc00", "-Infinity");
482 |
483 | oneNonFiniteTurn(0x7fc00000n, "f97e00", "NaN");
484 | oneNonFiniteTurn(0x7f800001n, "fa7f800001", "float'7f800001'");
485 | oneNonFiniteTurn(0xff800001n, "faff800001", "float'ff800001'");
486 | oneNonFiniteTurn(0x7fffffffn, "fa7fffffff", "float'7fffffff'");
487 | oneNonFiniteTurn(0xffc00000n, "f9fe00", "float'fe00'");
488 | oneNonFiniteTurn(0x7f800000n, "f97c00", "Infinity");
489 | oneNonFiniteTurn(0xff800000n, "f9fc00", "-Infinity");
490 |
491 | oneNonFiniteTurn(0x7ff8000000000000n, "f97e00", "NaN");
492 | oneNonFiniteTurn(0x7ff0000000000001n, "fb7ff0000000000001", "float'7ff0000000000001'");
493 | oneNonFiniteTurn(0xfff0000000000001n, "fbfff0000000000001", "float'fff0000000000001'");
494 | oneNonFiniteTurn(0x7fffffffffffffffn, "fb7fffffffffffffff", "float'7fffffffffffffff'");
495 | oneNonFiniteTurn(0x7ff0000020000000n, "fa7f800001", "float'7f800001'");
496 | oneNonFiniteTurn(0xfff0000020000000n, "faff800001", "float'ff800001'");
497 | oneNonFiniteTurn(0xfff8000000000000n, "f9fe00", "float'fe00'");
498 | oneNonFiniteTurn(0x7ff0040000000000n, "f97c01", "float'7c01'");
499 | oneNonFiniteTurn(0x7ff0000000000000n, "f97c00", "Infinity");
500 | oneNonFiniteTurn(0xfff0000000000000n, "f9fc00", "-Infinity");
501 |
502 | // Very special, some platforms natively support NaN with payloads, but we don't care
503 | // "signaling" NaN
504 | try {
505 | let nanWithPayload = new Uint8Array([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
506 | CBOR.Float.createExtendedFloat(new DataView(nanWithPayload.buffer, 0, 8).getFloat64(0, false));
507 | assertFalse("must not", supportNanWithPayloads);
508 | } catch (error) {
509 | assertTrue("not", error.toString().includes("payloads"));
510 | }
511 | let nonFinite = CBOR.Float.createExtendedFloat(Number.NaN);
512 | assertTrue("conv", nonFinite instanceof CBOR.NonFinite);
513 | assertTrue("truncated", nonFinite.getNonFinite64() == 0x7ff8000000000000n); // Returns "quiet" NaN
514 | assertTrue("cbor", CBOR.toHex(nonFinite.encode()) == "f97e00"); // Encoded as it should
515 | assertTrue("combined", Number.isNaN(nonFinite.getExtendedFloat64())); // Returns "Number"
516 | assertTrue("nan", nonFinite.isNaN(false)); // Indeed it is
517 |
518 | payloadOneTurn(0n, "f97c00", "Infinity");
519 | payloadOneTurn(1n, "f97e00", "NaN");
520 | payloadOneTurn(2n, "f97d00", null);
521 | payloadOneTurn((1n << 10n) - 1n, "f97fff", null);
522 | payloadOneTurn(1n << 10n, "fa7f801000", null);
523 | payloadOneTurn((1n << 23n) - 1n, "fa7fffffff", null);
524 | payloadOneTurn(1n << 23n, "fb7ff0000010000000", null);
525 | payloadOneTurn((1n << 52n) - 1n, "fb7fffffffffffffff", null);
526 |
527 | try {
528 | CBOR.NonFinite.createPayload(1n << 52n).encode();
529 | fail("pl8");
530 | } catch(error) {
531 | assertTrue("p18a", error.toString().includes("Payload out of range"));
532 | }
533 |
534 | success();
535 | `}
536 | ,
537 | {name:'hex.js',
538 | file:String.raw`// Test of "hex" utility methods
539 |
540 | const hex = '0123456789abcdefABCDEF';
541 |
542 | let bin = CBOR.fromHex(hex);
543 | let cnv = CBOR.toHex(bin);
544 | assertFalse("hex", CBOR.compareArrays(bin, CBOR.fromHex(cnv)));
545 | let ref = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef]);
546 | assertFalse("bin", CBOR.compareArrays(bin, ref));
547 | try {
548 | CBOR.fromHex("AAA");
549 | throw Error("should not");
550 | } catch (error) {
551 | if (!error.toString().includes("Unev")) {
552 | console.log(error);
553 | }
554 | }
555 |
556 | try {
557 | CBOR.fromHex("Ag");
558 | throw Error("should not");
559 | } catch (error) {
560 | if (!error.toString().includes("Bad hex")) {
561 | console.log(error);
562 | }
563 | }
564 | // Zero hex is accepted as well...
565 | assertFalse("zero", CBOR.compareArrays(CBOR.fromHex(''), new Uint8Array()));
566 | success();
567 | `}
568 | ,
569 | {name:'integer.js',
570 | file:String.raw`// Test program for integer "edge cases"
571 |
572 | function oneTurn(value, expected) {
573 | let text = value.toString();
574 | while (text.length < 25) {
575 | text += ' ';
576 | }
577 | let cbor = CBOR.BigInt(value).encode();
578 | let got = CBOR.toHex(cbor);
579 | if (got != expected) {
580 | got = '***=' + got;
581 | } else {
582 | got = '';
583 | }
584 | assertTrue("Failed decoding: " + value, CBOR.decode(cbor).getBigInt() == value);
585 | while (expected.length < 20) {
586 | expected += ' ';
587 | }
588 | if (got.length) {
589 | fail(text + expected + got);
590 | }
591 | }
592 | // -0 is treated as 0 for integers
593 | assertTrue("minus-0", CBOR.toHex(CBOR.Int(-0).encode()) == "00");
594 | oneTurn(0n, '00');
595 | oneTurn(-1n, '20');
596 | oneTurn(255n, '18ff');
597 | oneTurn(256n, '190100');
598 | oneTurn(-256n, '38ff');
599 | oneTurn(-257n, '390100');
600 | oneTurn(1099511627775n, '1b000000ffffffffff');
601 | oneTurn(18446744073709551615n, '1bffffffffffffffff');
602 | oneTurn(18446744073709551616n, 'c249010000000000000000');
603 | oneTurn(-18446744073709551616n, '3bffffffffffffffff');
604 | oneTurn(-18446744073709551617n, 'c349010000000000000000');
605 |
606 | try {
607 | CBOR.Int(1.1);
608 | fail("Should not");
609 | } catch (error) {
610 | assertTrue("msg1", error.toString().includes("Invalid integer: 1.1"));
611 | }
612 | try {
613 | CBOR.Int(Number.MAX_SAFE_INTEGER + 1);
614 | fail("Should not");
615 | } catch (error) {
616 | assertTrue("msg1", error.toString().includes("Invalid integer: " + (Number.MAX_SAFE_INTEGER + 1)));
617 | }
618 | try {
619 | CBOR.Int("10");
620 | fail("Should not");
621 | } catch (error) {
622 | assertTrue("msg2", error.toString().includes("Argument is not a 'number'"));
623 | }
624 | try {
625 | CBOR.BigInt("10");
626 | fail("Should not");
627 | } catch (error) {
628 | assertTrue("msg3", error.toString().includes("Argument is not a 'bigint'"));
629 | }
630 | try {
631 | CBOR.BigInt(1n, 7);
632 | fail("Should not");
633 | } catch (error) {
634 | assertTrue("msg4", error.toString().includes("CBOR.BigInt expects 1 argument(s)"));
635 | }
636 | try {
637 | CBOR.Int(1, 7);
638 | fail("Should not");
639 | } catch (error) {
640 | assertTrue("msg4", error.toString().includes("CBOR.Int expects 1 argument(s)"));
641 | }
642 |
643 | success();
644 | `}
645 | ,
646 | {name:'int-ranges.js',
647 | file:String.raw`// Testing range-constrained integers
648 |
649 | function goodRun(method, value) {
650 | let bigFlag = method.indexOf("64") > 0;
651 | let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
652 | let test = 'assertTrue("good", wrapper.' + method + '() == ' + (bigFlag ? value + 'n' : Number(value)) + ')';
653 | eval(test);
654 | }
655 |
656 | function badRun(method, value) {
657 | let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
658 | let test = 'wrapper.' + method + '()';
659 | try {
660 | eval(test);
661 | assertTrue("Should fail", false);
662 | } catch (error) {
663 | if (!error.toString().includes('Value out of range:') &&
664 | !error.toString().includes('Number.MAX_SAFE_INTEGER')) {
665 | throw error;
666 | }
667 | }
668 | }
669 |
670 | function innerTurn(method, signed, size) {
671 | let min = signed ? -(1n << BigInt(size) - 1n) : 0n;
672 | let max = signed ? (1n << BigInt(size) - 1n) - 1n : (1n << BigInt(size)) - 1n;
673 | if (size == 53) {
674 | max = BigInt(Number.MAX_SAFE_INTEGER);
675 | min = -max;
676 | }
677 | goodRun(method, min);
678 | goodRun(method, max);
679 | goodRun(method, 10n);
680 | badRun(method, max + 1n);
681 | badRun(method, min - 1n);
682 | }
683 |
684 | function oneTurn(size) {
685 | innerTurn("getInt" + size, true, size);
686 | if (size != 53) {
687 | innerTurn("getUint" + size, false, size);
688 | }
689 | }
690 |
691 | oneTurn(8);
692 | oneTurn(16);
693 | oneTurn(32);
694 | oneTurn(53);
695 | oneTurn(64);
696 |
697 | success();
698 | `}
699 | ,
700 | {name:'maps.js',
701 | file:String.raw`// Testing map operations
702 |
703 | let map = CBOR.Map()
704 | .set(CBOR.Int(3), CBOR.String("three"))
705 | .set(CBOR.Int(4), CBOR.String("four"));
706 | assertTrue("size-0", map.length == 2);
707 | let keys = map.getKeys();
708 | assertTrue("size-1", keys.length == 2);
709 | assertTrue("get-0", map.get(keys[0]).getString() == "three");
710 | assertTrue("get-1", map.get(keys[1]).getString() == "four");
711 |
712 | assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
713 | assertTrue("size-2", map.length == 1);
714 | assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
715 | assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
716 | assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
717 | assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
718 | map = map.merge(
719 | CBOR.Map().set(CBOR.Int(1), CBOR.String("hi")).set(CBOR.Int(5), CBOR.String("yeah")));
720 | assertTrue("size-3", map.length == 3);
721 | assertTrue("merge-0", map.get(CBOR.Int(1)).getString() == "hi");
722 | assertTrue("upd-0", map.update(CBOR.Int(1), CBOR.BigInt(-8n), true).getString() == "hi");
723 | assertTrue("upd-1", map.get(CBOR.Int(1)).getBigInt() == -8n);
724 | assertTrue("upd-2", map.update(CBOR.Int(10), CBOR.BigInt(-8n), false) == null);
725 | assertTrue("upd-3", map.get(CBOR.Int(10)).getBigInt() == -8n);
726 |
727 | function badKey(js) {
728 | try {
729 | eval(js);
730 | fail("Must fail!");
731 | } catch (error) {
732 | if (!error.toString().includes('Map key')) {
733 | throw error;
734 | }
735 | }
736 | }
737 |
738 | let immutableKey1 = CBOR.Array();
739 | let immutableKey2 = CBOR.Array();
740 | CBOR.Map().set(immutableKey1, CBOR.Int(4));
741 | badKey("immutableKey1.add(CBOR.Int(6))");
742 | let mutableValue = CBOR.Array();
743 | CBOR.Map().set(CBOR.Int(5), mutableValue);
744 | mutableValue.add(CBOR.Map());
745 | CBOR.Map().set(CBOR.Array().add(immutableKey2), CBOR.Int(5));
746 | badKey("immutableKey2.add(CBOR.Int(6))");
747 |
748 | success();
749 | `}
750 | ,
751 | {name:'arrays.js',
752 | file:String.raw`// Testing array operations
753 |
754 | let array = CBOR.Array()
755 | .add(CBOR.String("three"))
756 | .add(CBOR.String("four"));
757 | assertTrue("size-0", array.length == 2);
758 | assertTrue("get-0", array.get(0).getString() == "three");
759 | assertTrue("get-1", array.get(1).getString() == "four");
760 | let arrayElements = array.toArray();
761 | assertTrue("size-1", arrayElements.length == 2);
762 | assertTrue("arr-0", arrayElements[0].getString() == "three");
763 | assertTrue("arr-1", arrayElements[1].getString() == "four");
764 | assertTrue("upd-1", array.update(1, CBOR.Int(1)).getString() == "four");
765 | assertTrue("upd-2", array.get(1).getInt8() == 1);
766 | assertTrue("size-1", array.length == 2);
767 | assertTrue("upd-3", array.get(0).getString() == "three");
768 | assertTrue("upd-4", array.insert(array.length, CBOR.Int(-8)) == array);
769 | assertTrue("upd-5", array.get(array.length - 1).equals(CBOR.Int(-8)));
770 | assertTrue("upd-4", array.insert(0, CBOR.Int(-9)) == array);
771 | assertTrue("upd-5", array.get(0).equals(CBOR.Int(-9)));
772 | let l = array.length;
773 | assertTrue("upd-6", array.remove(0).equals(CBOR.Int(-9)));
774 | assertTrue("upd-7", l == array.length + 1);
775 | assertTrue("upd-8", array.get(0).getString() == "three");
776 | assertTrue("upd-9", array.toDiag(false) == '["three",1,-8]');
777 |
778 | function aBadOne(expression) {
779 | try {
780 | eval("array." + expression);
781 | fail("Should not pass");
782 | } catch (Error) {
783 | }
784 | }
785 |
786 | aBadOne("get('string')")
787 | aBadOne("get(array.length)");
788 | aBadOne("get(-1)");
789 | aBadOne("insert(array.length + 1, CBOR.Int(6))");
790 | aBadOne("insert(array.length)");
791 | aBadOne("remove(array.length)");
792 | aBadOne("remove(array.length - 1, 'hi')");
793 | aBadOne("get(0, 6)");
794 |
795 | /*
796 | assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
797 | assertTrue("size-2", map.length == 1);
798 | assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
799 | assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
800 | assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
801 | assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
802 | */
803 |
804 | success();
805 | `}
806 | ,
807 | {name:'miscellaneous.js',
808 | file:String.raw`// miscellaneous tests
809 |
810 | let bin = new Uint8Array([0xa5, 0x01, 0xd9, 0x01, 0xf4, 0x81, 0x18, 0x2d, 0x02, 0xf9, 0x80, 0x10,
811 | 0x04, 0x64, 0x53, 0x75, 0x72, 0x65, 0x05, 0xa2, 0x08, 0x69, 0x59, 0x65,
812 | 0x0a, 0x01, 0x61, 0x68, 0xe2, 0x82, 0xac, 0x09, 0x85, 0x66, 0x42, 0x79,
813 | 0x74, 0x65, 0x73, 0x21, 0x45, 0x01, 0x02, 0x03, 0x04, 0x05, 0xf5, 0xf4,
814 | 0xf6, 0x06, 0xc2, 0x4b, 0x66, 0x1e, 0xfd, 0xf2, 0xe3, 0xb1, 0x9f, 0x7c,
815 | 0x04, 0x5f, 0x15]);
816 |
817 | let cbor = CBOR.Map()
818 | .set(CBOR.Int(5),
819 | CBOR.Map()
820 | .set(CBOR.Int(8), CBOR.String("Ye\n\u0001ah€"))
821 | .set(CBOR.Int(9),
822 | CBOR.Array()
823 | .add(CBOR.String("Bytes!"))
824 | .add(CBOR.Bytes(new Uint8Array([1,2,3,4,5])))
825 | .add(CBOR.Boolean(true))
826 | .add(CBOR.Boolean(false))
827 | .add(CBOR.Null())))
828 | .set(CBOR.Int(4), CBOR.String("Sure"))
829 | .set(CBOR.Int(2), CBOR.Float(-9.5367431640625e-7))
830 | .set(CBOR.Int(6), CBOR.BigInt(123456789123456789123456789n))
831 | .set(CBOR.Int(1), CBOR.Tag(500n, CBOR.Array().add(CBOR.Int(45)))).encode();
832 | assertFalse("cmp1", CBOR.compareArrays(bin, cbor));
833 | let array = CBOR.decode(cbor).get(CBOR.Int(5)).get(CBOR.Int(9));
834 | assertTrue("bool1", array.get(2).getBoolean());
835 | assertFalse("bool1", array.get(3).getBoolean());
836 | assertFalse("null1", array.get(3).isNull());
837 | assertTrue("null2", array.get(4).isNull());
838 | assertFalse("cmp2", CBOR.compareArrays(CBOR.diagDecode(CBOR.decode(cbor).toString()).encode(), bin));
839 |
840 | assertTrue("version", CBOR.version == "1.0.16");
841 |
842 | success();
843 | `}
844 | ,
845 | {name:'nondeterministic.js',
846 | file:String.raw`// Testing "deterministic" code checks
847 |
848 | function oneTurn(hex, dn) {
849 | try {
850 | CBOR.decode(CBOR.fromHex(hex));
851 | throw Error("Should not fail on: " + dn);
852 | } catch (error) {
853 | if (!error.toString().includes("Non-d")) {
854 | throw error;
855 | }
856 | }
857 | let object = CBOR.initDecoder(CBOR.fromHex(hex),
858 | dn.includes("{") ? CBOR.LENIENT_MAP_DECODING : CBOR.LENIENT_NUMBER_DECODING).decodeWithOptions();
859 | if (object.toString() != dn || !object.equals(CBOR.decode(object.encode()))) {
860 | throw Error("non match:" + dn);
861 | }
862 | }
863 |
864 | oneTurn('1900ff', '255');
865 | oneTurn('1817', '23');
866 | oneTurn('A2026374776F01636F6E65', '{\n 1: "one",\n 2: "two"\n}');
867 | oneTurn('FB7FF8000000000000', 'NaN');
868 | oneTurn('FA7FC00000', 'NaN');
869 | oneTurn('FB3ff0000000000000', '1.0');
870 | oneTurn('c2480100000000000000', '72057594037927936');
871 | oneTurn('c24900ffffffffffffffff', '18446744073709551615');
872 | oneTurn('c240', '0');
873 |
874 | // This one is actually deterministic...
875 | try {
876 | oneTurn('fa7f7fffff', '3.4028234663852886e+38');
877 | } catch (error) {
878 | if (!error.toString().includes('Should not')) {
879 | throw error;
880 | }
881 | }
882 |
883 | success();
884 | `}
885 | ,
886 | {name:'out-of-range.js',
887 | file:String.raw`// Number overflow tests.
888 |
889 | const TOO_BIG = Number.MAX_SAFE_INTEGER + 1;
890 | const IN_RANGE = Number.MAX_SAFE_INTEGER;
891 |
892 | try {
893 | CBOR.Int(TOO_BIG);
894 | throw Error('Should not');
895 | } catch (error) {
896 | if (error.toString().includes('Should not')) {
897 | throw error;
898 | }
899 | }
900 | let cbor = CBOR.BigInt(BigInt(TOO_BIG)).encode();
901 | try {
902 | CBOR.decode(cbor).getInt53();
903 | throw Error('Should not');
904 | } catch (error) {
905 | if (error.toString().includes('Should not')) {
906 | throw error;
907 | }
908 | }
909 | assertTrue("big", BigInt(TOO_BIG) == CBOR.decode(cbor).getBigInt());
910 |
911 | cbor = CBOR.Int(IN_RANGE).encode();
912 | assertTrue("R0", CBOR.decode(cbor).getInt53() == IN_RANGE);
913 | cbor = CBOR.Int(-IN_RANGE).encode();
914 | assertTrue("R0", CBOR.decode(cbor).getInt53() == -IN_RANGE);
915 |
916 | success();
917 | `}
918 | ,
919 | {name:'sequence.js',
920 | file:String.raw`// Testing the "sequence" option
921 |
922 | let cbor = new Uint8Array([0x05, 0xa1, 0x05, 0x42, 0x6a, 0x6a])
923 | try {
924 | CBOR.decode(cbor);
925 | throw Error("Should not");
926 | } catch (error) {
927 | if (!error.toString().includes('Unexpected')) console.log(error);
928 | }
929 | let decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
930 | let total = new Uint8Array();
931 | let object;
932 | while (object = decoder.decodeWithOptions()) {
933 | total = CBOR.addArrays(total, object.encode());
934 | }
935 | assertFalse("Comp", CBOR.compareArrays(total, cbor));
936 | assertTrue("Comp2", total.length == decoder.getByteCount());
937 | decoder = CBOR.initDecoder(new Uint8Array(), CBOR.SEQUENCE_MODE);
938 | assertFalse("Comp3", decoder.decodeWithOptions());
939 | assertTrue("Comp4", decoder.getByteCount() == 0);
940 | let arraySequence = CBOR.Array();
941 | decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
942 | while (object = decoder.decodeWithOptions()) {
943 | arraySequence.add(object);
944 | }
945 | assertFalse("Comp5", CBOR.compareArrays(arraySequence.encodeAsSequence(), cbor));
946 |
947 | success();
948 | `}
949 | ,
950 | {name:'tags.js',
951 | file:String.raw`// Testing "tag"
952 |
953 | let object = CBOR.Array().add(CBOR.String("https://example.com/myobject")).add(CBOR.Int(6));
954 | let cbor = CBOR.Tag(CBOR.Tag.TAG_COTX, object).encode();
955 | let tag = CBOR.decode(cbor);
956 | assertTrue("t3", tag.getTagNumber()== CBOR.Tag.TAG_COTX);
957 | assertTrue("t3.1", object.equals(tag.get()));
958 | assertTrue("t3.2", tag.cotxId == "https://example.com/myobject");
959 | assertTrue("t3.3", tag.cotxObject.equals(CBOR.Int(6)));
960 | cbor = CBOR.Tag(0xf0123456789abcden, object).encode();
961 | assertTrue("t14", CBOR.decode(cbor).getTagNumber()== 0xf0123456789abcden);
962 | assertTrue("t5", CBOR.toHex(cbor) ==
963 | "dbf0123456789abcde82781c68747470733a2f2f6578616d706c652e636f6d2f6d796f626a65637406");
964 |
965 | [-1n, 0x10000000000000000n].forEach(tagNumber => {
966 | try {
967 | CBOR.Tag(tagNumber, CBOR.String("any"));
968 | throw Error("Should not");
969 | } catch (error) {
970 | if (!error.toString().includes("out of range")) {
971 | throw error;
972 | }
973 | }
974 | });
975 |
976 | [2n, 3n].forEach(tagNumber => {
977 | try {
978 | CBOR.Tag(tagNumber, CBOR.String("any"));
979 | throw Error("Should not");
980 | } catch (error) {
981 | if (!error.toString().includes("'bigint'")) {
982 | throw error;
983 | }
984 | }
985 | });
986 |
987 | [0n, 1n].forEach(tagNumber => {
988 | try {
989 | CBOR.Tag(tagNumber, CBOR.Boolean(true));
990 | throw Error("Should not");
991 | } catch (error) {
992 | if (!error.toString().includes("got: CBOR.Boolean")) {
993 | throw error;
994 | }
995 | }
996 | });
997 |
998 | success();
999 | `}
1000 | ,
1001 | {name:'simple.js',
1002 | file:String.raw`// Testing "simple"
1003 |
1004 | [-1, 256, 24, 31].forEach(value => {
1005 | try {
1006 | CBOR.Simple(value);
1007 | throw Error("Should not");
1008 | } catch (error) {
1009 | if (!error.toString().includes("out of range")) {
1010 | throw error;
1011 | }
1012 | }
1013 | });
1014 |
1015 | function oneTurn(value, hex) {
1016 | let s = CBOR.Simple(value);
1017 | let s2 = CBOR.decode(s.encode());
1018 | assertTrue("v", s.getSimple() == value);
1019 | assertTrue("v2", s2.getSimple() == value);
1020 | assertTrue("b", CBOR.toHex(s2.encode()) == hex);
1021 | }
1022 |
1023 | oneTurn(0, "e0");
1024 | oneTurn(23, "f7");
1025 | oneTurn(32, "f820");
1026 | oneTurn(255, "f8ff");
1027 |
1028 | success();
1029 | `}
1030 | ,
1031 | {name:'dates.js',
1032 | file:String.raw`// Testing instant methods
1033 |
1034 | function oneGetDateTime(epoch, isoString) {
1035 | assertTrue("date1", CBOR.String(isoString).getDateTime().getTime() == epoch);
1036 | let cbor = CBOR.decode(CBOR.String(isoString).encode());
1037 | assertTrue("date2", cbor.getDateTime().getTime() == epoch);
1038 | assertTrue("date3", CBOR.Tag(0n, CBOR.String(isoString)).getDateTime().getTime() == epoch);
1039 | }
1040 |
1041 | function badDate(hexBor, err) {
1042 | try {
1043 | CBOR.decode(CBOR.fromHex(hexBor));
1044 | fail("must not");
1045 | } catch (error) {
1046 | if (!error.toString().includes(err)) {
1047 | throw error;
1048 | }
1049 | }
1050 | }
1051 |
1052 | function truncateDateTime(iso, millis, seconds) {
1053 | let dateTime = CBOR.String(iso).getDateTime();
1054 | assertTrue("trdt1", dateTime.getTime() == millis);
1055 | assertTrue("trdt2", CBOR.createDateTime(dateTime, true, false).getDateTime().getTime() == millis);
1056 | assertTrue("trdt3", CBOR.createDateTime(dateTime, false, false).getDateTime().getTime() == seconds * 1000);
1057 | }
1058 |
1059 | function truncateEpochTime(float, millis, seconds) {
1060 | let epoch = CBOR.Float(float).getEpochTime();
1061 | assertTrue("tr1", epoch.getTime() == millis);
1062 | assertTrue("tr2", CBOR.createEpochTime(epoch, true).getEpochTime().getTime() == millis);
1063 | assertTrue("tr3", CBOR.createEpochTime(epoch, false).getEpochTime().getTime() == seconds * 1000);
1064 | }
1065 |
1066 | function oneGetEpochTime(hexBor, epoch, err) {
1067 | let time = Math.floor((epoch * 1000) + 0.5);
1068 | let instant = CBOR.decode(CBOR.fromHex(hexBor)).getEpochTime();
1069 | assertTrue("epoch1", instant.getTime() == time);
1070 | let cborObject = CBOR.createEpochTime(instant, time % 1000);
1071 | // console.log("E=" + cborObject.toString());
1072 | // console.log("1=" + cborObject.getEpochTime().getTime() + " 2=" + time);
1073 | assertTrue("epoch2", cborObject.getEpochTime().getTime() == time);
1074 | if (time % 1000 > 500) {
1075 | let p1 = Math.floor(epoch + 1.0) * 1000;
1076 | cborObject = CBOR.createEpochTime(instant, false);
1077 | // console.log("r1=" + cborObject.getEpochTime().getTime() + " r2=" + p1);
1078 | assertTrue("epoch3", cborObject.getEpochTime().getTime() == p1);
1079 | }
1080 | instant = CBOR.decode(CBOR.fromHex(hexBor));
1081 | try {
1082 | instant.checkForUnread();
1083 | fail("must not");
1084 | } catch (error) {
1085 | if (!error.toString().includes(err)) {
1086 | throw error;
1087 | }
1088 | }
1089 | instant.getEpochTime();
1090 | instant.checkForUnread();
1091 | }
1092 |
1093 | oneGetDateTime(1740060548000, "2025-02-20T14:09:08+00:00");
1094 | oneGetDateTime(1740060548000, "2025-02-20T14:09:08Z");
1095 | oneGetDateTime(1740060548000, "2025-02-20T15:09:08+01:00");
1096 | oneGetDateTime(1740060548000, "2025-02-20T15:39:08+01:30");
1097 | oneGetDateTime(1740060548000, "2025-02-20T12:09:08-02:00");
1098 | oneGetDateTime(1740060548000, "2025-02-20T11:39:08-02:30");
1099 | oneGetDateTime(1740060548123, "2025-02-20T11:39:08.123-02:30");
1100 | oneGetDateTime(1740060548930, "2025-02-20T14:09:08.930Z");
1101 | // Next: Truncates!
1102 | oneGetDateTime(1740060548930, "2025-02-20T14:09:08.9305Z");
1103 | oneGetDateTime(-62167219200000, "0000-01-01T00:00:00Z");
1104 | oneGetDateTime(253402300799000, "9999-12-31T23:59:59Z");
1105 |
1106 | badDate("c001", "got: CBOR.Int");
1107 | badDate("c06135", "Invalid ISO date string: 5");
1108 | badDate("c16135", "got: CBOR.String");
1109 |
1110 | oneGetEpochTime("1A67B73784", 1740060548, "Data of type=CBOR.Int");
1111 | oneGetEpochTime("FB41D9EDCDE113645A", 1740060548.303, "Data of type=CBOR.Float with value=174");
1112 | oneGetEpochTime("c1FB41D9EDCDE113645A", 1740060548.303, "Tagged object 1 of type=CBOR.Float");
1113 | // Next: Truncates!
1114 | // oneGetEpochTime("c1fb41d9edcde1136c8b", 1740060548.3035, "Tagged object 1 of type=CBOR.Float");
1115 | // oneGetEpochTime("c1fb41d9edcde1204189", 1740060548.5045, "Tagged object 1 of type=CBOR.Float");
1116 | oneGetEpochTime("c11b0000003afff4417f", 253402300799, "Tagged object 1 of type=CBOR.Int");
1117 | oneGetEpochTime("00", 0, "Data of type=CBOR.Int");
1118 |
1119 | function oneMillis(time, iso) {
1120 | let instant = new Date();
1121 | instant.setTime(time);
1122 | assertTrue("cdt1=", CBOR.createDateTime(instant, true, true).getString() == iso);
1123 | let created = CBOR.createDateTime(instant, true, false);
1124 | assertTrue("cdt2=", created.getDateTime().getTime() == time);
1125 | assertTrue("cdt3=", created.getString().length == iso.length + 5);
1126 | created = CBOR.createEpochTime(instant, true);
1127 | assertTrue("cet1=", created.getEpochTime().getTime() == time);
1128 | assertTrue("cet2=", created instanceof CBOR.Float == iso.includes("."));
1129 | }
1130 |
1131 | oneMillis(1752189147000, "2025-07-10T23:12:27Z");
1132 | oneMillis(1752189147123, "2025-07-10T23:12:27.123Z");
1133 | oneMillis(1752189147120, "2025-07-10T23:12:27.12Z");
1134 | oneMillis(1752189147100, "2025-07-10T23:12:27.1Z");
1135 |
1136 | truncateEpochTime(1740060548.000, 1740060548000, 1740060548);
1137 | truncateEpochTime(0.0, 0, 0);
1138 | truncateEpochTime(1740060548.0004, 1740060548000, 1740060548);
1139 | truncateEpochTime(1740060548.0005, 1740060548001, 1740060548);
1140 |
1141 | truncateDateTime("2025-07-10T23:12:27Z", 1752189147000, 1752189147);
1142 | truncateDateTime("2025-07-10T23:12:27.1Z", 1752189147100, 1752189147);
1143 | truncateDateTime("2025-07-10T23:12:27.12Z", 1752189147120, 1752189147);
1144 | truncateDateTime("2025-07-10T23:12:27.123Z", 1752189147123, 1752189147);
1145 | truncateDateTime("2025-07-10T23:12:27.1233Z", 1752189147123, 1752189147);
1146 | truncateDateTime("2025-07-10T23:12:27.1235Z", 1752189147123, 1752189147);
1147 | truncateDateTime("2025-07-10T23:12:27.523Z", 1752189147523, 1752189148);
1148 |
1149 | truncateDateTime("1925-07-10T23:12:27Z", -1403570853000, -1403570853);
1150 | truncateDateTime("1925-07-10T23:12:27.1Z", -1403570852900, -1403570853);
1151 | truncateDateTime("1925-07-10T23:12:27.12Z", -1403570852880, -1403570853);
1152 | truncateDateTime("1925-07-10T23:12:27.123Z", -1403570852877, -1403570853);
1153 | truncateDateTime("1925-07-10T23:12:27.1233Z", -1403570852877, -1403570853);
1154 | truncateDateTime("1925-07-10T23:12:27.1235Z", -1403570852877, -1403570853);
1155 | truncateDateTime("1925-07-10T23:12:27.499Z", -1403570852501, -1403570853);
1156 | truncateDateTime("1925-07-10T23:12:27.500Z", -1403570852500, -1403570852);
1157 | truncateDateTime("1925-07-10T23:12:27.700Z", -1403570852300, -1403570852);
1158 |
1159 | try {
1160 | // Z or -+local offset needed.
1161 | CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43"));
1162 | throw Error("Should not");
1163 | } catch (error) {
1164 | if (!error.toString().includes("ISO")) {
1165 | throw error;
1166 | }
1167 | }
1168 |
1169 | try {
1170 | // Beyond nano-seconds
1171 | CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43.6666666666Z"));
1172 | throw Error("Should not");
1173 | } catch (error) {
1174 | if (!error.toString().includes("ISO")) {
1175 | throw error;
1176 | }
1177 | }
1178 |
1179 | try {
1180 | // 24 hour is incorrect.
1181 | CBOR.Tag(0n, CBOR.String("2023-06-22T24:01:43Z"));
1182 | throw Error("Should not");
1183 | } catch (error) {
1184 | if (!error.toString().includes("ISO")) {
1185 | throw error;
1186 | }
1187 | }
1188 |
1189 | [-1, 253402300800].forEach(epoch => {
1190 | try {
1191 | // Out of range for Date().
1192 | CBOR.Tag(1n, CBOR.Int(epoch));
1193 | throw Error("Should not");
1194 | } catch (error) {
1195 | if (!error.toString().includes("Epoch out of")) {
1196 | throw error;
1197 | }
1198 | }
1199 | try {
1200 | // Out of range for Date().
1201 | let instant = new Date();
1202 | instant.setTime(epoch * 1000);
1203 | CBOR.createEpochTime(instant, true);
1204 | throw Error("Should not");
1205 | } catch (error) {
1206 | if (!error.toString().includes("Epoch out of")) {
1207 | throw error;
1208 | }
1209 | }
1210 | });
1211 |
1212 | assertTrue("zero", CBOR.String("0000-01-01T00:00:00Z").getDateTime().getTime() == -62167219200000);
1213 |
1214 | let now = new Date();
1215 | /*
1216 | console.log("Now=" + now.getTime() + " iso=" + now.toISOString() +
1217 | " offset=" + now.getTimezoneOffset() + " str=" + now.toString());
1218 | */
1219 |
1220 | function oneCreateDateTime(dateOrTime, utc, millis, bad) {
1221 | let instant = new Date();
1222 | let time = typeof dateOrTime == 'number' ? Math.round(dateOrTime) : dateOrTime.getTime();
1223 | instant.setTime(time);
1224 | if (bad) {
1225 | try {
1226 | CBOR.createDateTime(instant, millis, utc);
1227 | throw Error("Should not");
1228 | } catch (error) {
1229 | if (!error.toString().includes("Date object out of range")) {
1230 | throw error;
1231 | }
1232 | }
1233 | } else {
1234 | let dateTime = CBOR.createDateTime(instant, millis, utc);
1235 | if (millis || !(time % 1000)) {
1236 | assertTrue("cdt1" + dateTime, dateTime.getDateTime().getTime() == time);
1237 | } else if (!millis && time % 1000) {
1238 | assertFalse("cdt2" + dateTime, dateTime.getDateTime().getTime() == time);
1239 | }
1240 | }
1241 | }
1242 |
1243 | oneCreateDateTime(now, true, false);
1244 | oneCreateDateTime(now, true, true);
1245 | oneCreateDateTime(now, false, false);
1246 | oneCreateDateTime(now, false, true);
1247 |
1248 | oneCreateDateTime(1740060548000, true, false);
1249 | oneCreateDateTime(1740060548000, true, true);
1250 | oneCreateDateTime(1740060548501, true, false);
1251 | oneCreateDateTime(1740060548501, true, true);
1252 | oneCreateDateTime(1740060548000.3, true, true);
1253 | oneCreateDateTime(1740060548000.5, true, true);
1254 | oneCreateDateTime(-62167219200000, true, true);
1255 | oneCreateDateTime(-62167219200001, true, true, true);
1256 | oneCreateDateTime(253402300799000, true, true);
1257 | oneCreateDateTime(253402300799001, true, true, true);
1258 |
1259 | success();`}
1260 | ,
1261 | {name:'dynamic.js',
1262 | file:String.raw`// dynamic tests
1263 |
1264 | assertTrue("dyn", CBOR.Map().setDynamic(wr =>
1265 | wr.set(CBOR.Int(1), CBOR.Boolean(true))).get(CBOR.Int(1)).getBoolean());
1266 |
1267 | let option = "on";
1268 | assertTrue("dyn", CBOR.Map().setDynamic(wr => {
1269 | if (option) {
1270 | wr.set(CBOR.Int(1), CBOR.String(option));
1271 | }
1272 | return wr;
1273 | }).get(CBOR.Int(1)).getString() == option);
1274 |
1275 | function lambda(wr) {
1276 | wr.set(CBOR.Int(1), CBOR.Boolean(true));
1277 | return wr;
1278 | }
1279 | assertTrue("dyn", CBOR.Map().setDynamic(lambda).get(CBOR.Int(1)).getBoolean());
1280 |
1281 | success();
1282 | `}
1283 | ,
1284 | {name:'utf8.js',
1285 | file:String.raw`// Test of "utf8" converters
1286 |
1287 | function utf8EncoderTest(string, ok) {
1288 | try {
1289 | CBOR.String(string).encode();
1290 | assertTrue("enc", ok);
1291 | } catch (error) {
1292 | assertFalse("No good", ok);
1293 | }
1294 |
1295 | }
1296 |
1297 | function utf8DecoderTest(hex, ok) {
1298 | let cbor = CBOR.fromHex(hex);
1299 | let roundTrip;
1300 | try {
1301 | roundTrip = CBOR.decode(cbor).encode();
1302 | } catch (error) {
1303 | assertFalse("No good", ok);
1304 | return;
1305 | }
1306 | assertTrue("OK", ok);
1307 | assertFalse("Conv", CBOR.compareArrays(cbor, roundTrip));
1308 | }
1309 |
1310 | utf8DecoderTest("62c328", false);
1311 | utf8DecoderTest("64f0288cbc", false);
1312 | utf8DecoderTest("64f0908cbc", true);
1313 | utf8EncoderTest("Hi", true)
1314 | utf8EncoderTest("\uD83D", false);
1315 | utf8EncoderTest("\uD83D\uDE2D", true);
1316 |
1317 | success();
1318 | `}
1319 | ,
1320 | {name:'xyz-encoder.js',
1321 | file:String.raw`// Simple "encoder" API
1322 |
1323 | class XYZEncoder {
1324 |
1325 | static COUNTER = CBOR.Int(1);
1326 | static TEMPERATURE = CBOR.Int(2);
1327 | static GREETING = CBOR.Int(3);
1328 |
1329 | #map;
1330 |
1331 | constructor() {
1332 | this.#map = CBOR.Map();
1333 | }
1334 |
1335 | setCounter = function(intVal) {
1336 | this.#map.set(XYZEncoder.COUNTER, CBOR.Int(intVal));
1337 | return this;
1338 | }
1339 |
1340 | setTemperature = function(floatVal) {
1341 | this.#map.set(XYZEncoder.TEMPERATURE, CBOR.Float(floatVal));
1342 | return this;
1343 | }
1344 |
1345 | setGreeting = function(stringVal) {
1346 | this.#map.set(XYZEncoder.GREETING, CBOR.String(stringVal));
1347 | return this;
1348 | }
1349 |
1350 | encode = function() {
1351 | assertTrue("incomplete", this.#map.length == 3);
1352 | return this.#map.encode();
1353 | }
1354 | }
1355 |
1356 | let cbor = new XYZEncoder()
1357 | .setCounter(2)
1358 | .setGreeting('Hi!')
1359 | .setTemperature(53.0001)
1360 | .encode();
1361 |
1362 | assertTrue("bad code", CBOR.toHex(cbor) == 'a3010202fb404a800346dc5d640363486921');
1363 |
1364 | success();
1365 | `}
1366 | ,
1367 | {name:'xyz-decoder.js',
1368 | file:String.raw`// Simple "decoder" API
1369 |
1370 | class XYZDecoder {
1371 |
1372 | static COUNTER = CBOR.Int(1);
1373 | static TEMPERATURE = CBOR.Int(2);
1374 | static GREETING = CBOR.Int(3);
1375 |
1376 | #counter;
1377 | #temperature;
1378 | #greeting;
1379 |
1380 | constructor(cbor) {
1381 | // There MUST be exactly three key/value pairs.
1382 | // CBOR data items are type-checked as well.
1383 | let map = CBOR.decode(cbor);
1384 | // If the top-level object is not a CBOR map, the next
1385 | // JavaScript line will throw an exception because there is
1386 | // only one get-method that has a CBOR wrapper as input parameter.
1387 | this.#counter = map.get(XYZDecoder.COUNTER).getUint8();
1388 | this.#temperature = map.get(XYZDecoder.TEMPERATURE).getFloat64();
1389 | this.#greeting = map.get(XYZDecoder.GREETING).getString();
1390 | // We got more than we asked for?
1391 | map.checkForUnread();
1392 | }
1393 |
1394 | get counter() {
1395 | return this.#counter;
1396 | }
1397 |
1398 | get temperature() {
1399 | return this.#temperature;
1400 | }
1401 |
1402 | get greeting() {
1403 | return this.#greeting;
1404 | }
1405 |
1406 | }
1407 |
1408 | let cbor = CBOR.fromHex('a3010202fb404a800346dc5d640363486921');
1409 |
1410 | let xyz = new XYZDecoder(cbor);
1411 |
1412 | assertTrue("counter", xyz.counter == 2);
1413 | assertTrue("temperature", xyz.temperature == 53.0001);
1414 | assertTrue("greeting", xyz.greeting == 'Hi!');
1415 |
1416 | success();
1417 | `}
1418 |
1419 | ];
1420 |
1421 | function runTest() {
1422 | test = 0;
1423 | failures = 0;
1424 | for (let test = 0; test < TESTS.length; test++) {
1425 | name = TESTS[test].name;
1426 | try {
1427 | eval(TESTS[test].file);
1428 | } catch (error) {
1429 | failures++;
1430 | console.log(name + " FAILED: " + error);
1431 | }
1432 | }
1433 | if (failures) {
1434 | console.log('There were ' + failures + ' errors');
1435 | } else {
1436 | console.log('PASSED');
1437 | }
1438 | }
1439 |
1440 | runTest();
1441 |
--------------------------------------------------------------------------------
/test/test-all.js:
--------------------------------------------------------------------------------
1 | // Testing CBOR.js API
2 | import CBOR from '../npm/mjs/index.mjs';
3 |
4 | let failures = 0;
5 | let test = 0;
6 | let name = '';
7 |
8 | function assertTrue(text, bool) {
9 | if (!bool) throw Error("Assertion: " + text);
10 | }
11 |
12 | function assertFalse(text, bool) {
13 | if (bool) throw Error("Assertion: " + text);
14 | }
15 |
16 | function fail(text) {
17 | throw Error("Fail: " + text);
18 | }
19 |
20 | function success() {
21 | console.log('Test ' + name + ' was successful');
22 | }
23 |
24 | let TESTS=[
25 |
26 | {name:'base64.js',
27 | file:String.raw`// Testing the B64U/B64 converters
28 |
29 | let bin = new Uint8Array(256);
30 | for (let i = 0; i < bin.length; i++) {
31 | bin[i] = i;
32 | }
33 | let b64U = CBOR.toBase64Url(bin);
34 | assertFalse("cmp1", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64U)));
35 |
36 | // This is what "btoa" returns for bin:
37 | let b64 = 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissL\
38 | S4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY\
39 | 2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYm\
40 | ZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz\
41 | 9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==';
42 |
43 | // fromBase64Url is "permissive" and takes Base64 with padding as well...
44 | assertFalse("cmp2", CBOR.compareArrays(bin, CBOR.fromBase64Url(b64)));
45 |
46 | assertFalse("cmp3", CBOR.compareArrays(CBOR.fromBase64Url('oQVkZGF0YQ'),
47 | CBOR.fromHex('a1056464617461')));
48 | // Zero data is compliant
49 | assertFalse("cmp4", CBOR.compareArrays(CBOR.fromBase64Url(''), new Uint8Array()));
50 | assertTrue("cmp4", CBOR.toBase64Url(new Uint8Array()) == "");
51 | success();
52 | `}
53 | ,
54 | {name:'check-for-unread.js',
55 | file:String.raw`// Testing the "checkForUnread()" feature
56 |
57 | function oneTurn(create, access, errorString) {
58 | let res = eval(create);
59 | try {
60 | res.checkForUnread();
61 | if (errorString !== null) {
62 | throw Error("no way");
63 | }
64 | } catch (error) {
65 | if (!error.toString().includes('never read')) {
66 | throw error;
67 | }
68 | }
69 | try {
70 | eval(access);
71 | res.checkForUnread();
72 | assertFalse("cfu1", errorString);
73 | } catch (error) {
74 | assertTrue("cfu2=" + error, errorString);
75 | if (!error.toString().includes(errorString)) {
76 | throw error;
77 | }
78 | }
79 | eval(create).scan().checkForUnread();
80 | }
81 |
82 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
83 | "res.get(0).get(CBOR.Int(1)).getString()");
84 |
85 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
86 | "res",
87 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
88 |
89 | oneTurn("CBOR.Array().add(CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
90 | "res.get(0).get(CBOR.Int(1))",
91 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
92 |
93 | oneTurn("CBOR.Array().add(CBOR.Map())",
94 | "res",
95 | "Array element of type=CBOR.Map with value={} was never read");
96 |
97 | // Empty Map => nothing to read
98 | oneTurn("CBOR.Array().add(CBOR.Map())",
99 | "res.get(0)",
100 | "Array element of type=CBOR.Map with value={} was never read");
101 |
102 | oneTurn("CBOR.Array().add(CBOR.Map())",
103 | "res.get(0).scan()",
104 | null);
105 |
106 | // Empty Array => nothing to read
107 | oneTurn("CBOR.Array()",
108 | "res",
109 | "Data of type=CBOR.Array with value=[] was never read");
110 |
111 | oneTurn("CBOR.Array()",
112 | "res.scan()",
113 | null);
114 |
115 | oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
116 | "res.get().get(CBOR.Int(1)).getString()");
117 |
118 | oneTurn("CBOR.Tag(8n, CBOR.Map().set(CBOR.Int(1), CBOR.String('hi')))",
119 | "res.get()",
120 | "Map key 1 with argument of type=CBOR.String with value=\"hi\" was never read");
121 |
122 | oneTurn("CBOR.Tag(8n, CBOR.Map())",
123 | "res.get()",
124 | "Tagged object 8 of type=CBOR.Map with value={} was never read");
125 |
126 | oneTurn("CBOR.Simple(8)",
127 | "res",
128 | "Data of type=CBOR.Simple with value=simple(8) was never read");
129 |
130 | oneTurn("CBOR.Simple(8)",
131 | "res.getSimple()",
132 | null);
133 |
134 | oneTurn("CBOR.Tag(8n, CBOR.Map())",
135 | "res.get().scan()",
136 | null);
137 |
138 | // Date time specials
139 | oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
140 | "res.get()",
141 | "Tagged object 0 of type=CBOR.String with value=\"2025-02-20T14:09:08Z\" was never read");
142 |
143 | oneTurn("CBOR.Tag(0n, CBOR.String(\"2025-02-20T14:09:08Z\"))",
144 | "res.get().getString()",
145 | null);
146 |
147 | oneTurn("CBOR.Tag(8n, CBOR.Int(2))",
148 | "res.get()",
149 | "Tagged object 8 of type=CBOR.Int with value=2 was never read");
150 |
151 | oneTurn("CBOR.Int(1)",
152 | "res.getInt32()");
153 | success();
154 | `}
155 | ,
156 | {name:'clone.js',
157 | file:String.raw`// Testing the "clone()" and "equals() methods
158 |
159 | let object = CBOR.Map()
160 | .set(CBOR.Int(2), CBOR.Array()
161 | .add(CBOR.Boolean(false)));
162 | assertTrue("clone+equals", object.equals(object.clone()));
163 | let copy = object.clone().set(CBOR.Int(1), CBOR.String("Hi"));
164 | assertFalse("copy+equals+clone", copy.equals(object));
165 |
166 | success();
167 | `}
168 | ,
169 | {name:'cotx.js',
170 | file:String.raw`// Testing the COTX identifier
171 |
172 | function oneTurn(hex, dn, ok) {
173 | try {
174 | let object = CBOR.decode(CBOR.fromHex(hex));
175 | assertTrue("Should not execute", ok);
176 | if (object.toString() != dn.toString() || !object.equals(CBOR.decode(object.encode()))) {
177 | throw Error("non match:" + dn + " " + object.toString());
178 | }
179 | } catch (error) {
180 | if (ok) console.log(error.toString());
181 | assertFalse("Must succeed", ok);
182 | }
183 | }
184 |
185 | oneTurn('d903f2623737', '1010("77")', false);
186 | oneTurn('d903f281623737', '1010(["77"])', false);
187 | oneTurn('d903f28206623737', '1010([6, "77"])', false);
188 | oneTurn('d903f28262373707', '1010(["77", 7])', true);
189 |
190 | success();
191 | `}
192 | ,
193 | {name:'diagnostic.js',
194 | file:String.raw`// Testing "diagnostic notation"
195 |
196 | function oneTurn(cborText, ok, compareWithOrNull) {
197 | try {
198 | let compareText = compareWithOrNull ? compareWithOrNull : cborText;
199 | let result = CBOR.diagDecode(cborText);
200 | assertTrue("Should not", ok);
201 | let sequence = CBOR.diagDecodeSequence(cborText);
202 | if (result.toString() != compareText) {
203 | throw Error("input:\n" + cborText + "\nresult:\n" + result);
204 | }
205 | assertTrue("seq", sequence.length == 1);
206 | if (sequence[0].toString() != compareText) {
207 | throw Error("input:\n" + cborText + "\nresult:\n" + result);
208 | }
209 | } catch (error) {
210 | assertFalse("Err: " + error, ok);
211 | }
212 | }
213 |
214 | function oneBinaryTurn(diag, hex) {
215 | assertTrue("bin", CBOR.toHex(CBOR.diagDecode(diag).encode()) == hex);
216 | }
217 |
218 | oneTurn("2", true, null);
219 | oneTurn("2.0", true, null);
220 | oneTurn("123456789012345678901234567890", true, null);
221 | oneTurn("Infinity", true, null);
222 | oneTurn("-Infinity", true, null);
223 | oneTurn("NaN", true, null);
224 | oneTurn("0.0", true, null);
225 | oneTurn("-0.0", true, null);
226 | oneTurn('{\n 4: "hi"\n}', true, null);
227 | oneTurn('[4, true, false, null]', true, null);
228 | oneTurn('"next\nline\r\\\ncont\r\nk"', true, '"next\\nline\\ncont\\nk"');
229 | oneTurn('{1:<< 5 , 7 >>}', true, "{\n 1: h'0507'\n}");
230 | oneTurn('<<[3.0]>>', true, "h'81f94200'");
231 | oneTurn('0b100_000000001', true, "2049");
232 | oneTurn('4.0e+500', false, null);
233 | oneTurn('4.0e+5', true, "400000.0");
234 | oneTurn('"missing', false, null);
235 | oneTurn('simple(21)', true, 'true');
236 | oneTurn('simple(59)', true, 'simple(59)');
237 | oneBinaryTurn('"\\ud800\\udd51"', "64f0908591");
238 | oneBinaryTurn("'\\u20ac'", "43e282ac");
239 | oneBinaryTurn('"\\"\\\\\\b\\f\\n\\r\\t"', "67225c080c0a0d09");
240 |
241 | let cborObject = CBOR.decode(CBOR.fromHex('a20169746578740a6e6578740284fa3380000147a10564646\
242 | 17461a1f5f4c074323032332d30362d30325430373a35333a31395a'));
243 |
244 | let cborText = '{\n' +
245 | ' 1: "text\\nnext",\n' +
246 | ' 2: [\n' +
247 | ' 5.960465188081798e-8,\n' +
248 | ' h\'a1056464617461\',\n' +
249 | ' {\n' +
250 | ' true: false\n' +
251 | ' },\n' +
252 | ' 0("2023-06-02T07:53:19Z")\n' +
253 | ' ]\n' +
254 | '}';
255 |
256 | assertTrue("pretty", cborObject.toDiag(true) == cborText);
257 | assertTrue("oneline", cborObject.toDiag(false) ==
258 | cborText.replaceAll('\n', '').replaceAll(' ',''));
259 | assertTrue("parse", CBOR.diagDecode(cborText).equals(cborObject));
260 | let sequence = CBOR.diagDecodeSequence('45,{4:7}');
261 | assertTrue("seq2", sequence.length == 2);
262 | assertTrue("seq3", sequence[0].getInt32() == 45);
263 | assertTrue("seq4", sequence[1].equals(CBOR.Map().set(CBOR.Int(4),CBOR.Int(7))));
264 |
265 | try {
266 | CBOR.diagDecode("float'000000'");
267 | fail("bugf");
268 | } catch (error) {
269 | assertTrue("fp", error.toString().includes('floating-point'));
270 | }
271 |
272 | success();
273 | `}
274 | ,
275 | {name:'float.js',
276 | file:String.raw`// Test program for floating-point "edge cases"
277 |
278 | function overflow(decodedValue, length) {
279 | let test = 'decodedValue.getFloat' + length + '()';
280 | try {
281 | eval(test);
282 | assertTrue("Should fail", false);
283 | } catch (error) {
284 | if (!error.toString().includes('Value out of range:')) {
285 | throw error;
286 | }
287 | }
288 | }
289 |
290 | function shouldpass(decodedValue, value, length, valueText) {
291 | assertTrue("p1", decodedValue.toString() == valueText);
292 | let test = 'decodedValue.getFloat' + length + '()';
293 | let float = eval(test);
294 | assertTrue("p2", float == value);
295 | if (length == "64") {
296 | test = 'decodedValue.getExtendedFloat' + length + '()';
297 | float = eval(test);
298 | assertTrue("p3", float == value);
299 | }
300 | }
301 |
302 | function oneTurn(valueText, expected) {
303 | let value = Number(valueText);
304 | if (Number.isFinite(value)) {
305 | try {
306 | CBOR.NonFinite(value);
307 | fail("f1");
308 | } catch (error) {
309 | assertTrue("f2", error.toString().includes("bigint"));
310 | }
311 | let cbor = CBOR.Float(value).encode();
312 | assertTrue("f3", CBOR.toHex(cbor) == expected);
313 | let decodedValue = CBOR.decode(cbor);
314 | switch (cbor.length) {
315 | case 3:
316 | shouldpass(decodedValue, value, "16", valueText);
317 | shouldpass(decodedValue, value, "32", valueText);
318 | shouldpass(decodedValue, value, "64", valueText);
319 | break;
320 |
321 | case 5:
322 | shouldpass(decodedValue, value, "32", valueText);
323 | shouldpass(decodedValue, value, "64", valueText);
324 | overflow(decodedValue, "16");
325 | break;
326 |
327 | case 9:
328 | shouldpass(decodedValue, value, "64", valueText);
329 | overflow(decodedValue, "16");
330 | overflow(decodedValue, "32");
331 | break;
332 |
333 | default:
334 | fail("No such length");
335 | }
336 | } else {
337 | try {
338 | CBOR.Float(value);
339 | fail('Should not execute');
340 | } catch (error) {
341 | assertTrue("nf1", error.toString().includes('CBOR.NonFinite'));
342 | }
343 | let decodedValue = CBOR.Float.createExtendedFloat(value);
344 | assertTrue("nf2", decodedValue.getExtendedFloat64().toString() == value.toString());
345 | assertTrue("nf3", decodedValue.toString() == value.toString());
346 | let cbor = decodedValue.encode();
347 | assertTrue("nf4", CBOR.toHex(cbor) == expected);
348 | assertTrue("nf5", CBOR.decode(cbor).equals(decodedValue));
349 | let buf = new Uint8Array(8);
350 | new DataView(buf.buffer, 0, 8).setFloat64(0, value, false);
351 | assertTrue("nf6", decodedValue.getNonFinite64() == CBOR.toBigInt(buf));
352 | }
353 | assertTrue("d10", CBOR.toHex(CBOR.Float.createExtendedFloat(value).encode()) == expected);
354 | }
355 |
356 | const inNanWithPayload = new Uint8Array([0x7f, 0xf8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]);
357 |
358 | let value = new DataView(inNanWithPayload.buffer, 0, 8).getFloat64(0, false);
359 |
360 | let outNanWithPayload = new Uint8Array(8);
361 | new DataView(outNanWithPayload.buffer, 0, 8).setFloat64(0, value, false);
362 |
363 | let supportNanWithPayloads = true;
364 | for (let q = 0; q < 8; q++) {
365 | if (inNanWithPayload[q] != outNanWithPayload[q]) {
366 | // console.log(outNanWithPayload.toString());
367 | console.log('This implementation does not support NaN with payloads');
368 | supportNanWithPayloads = false;
369 | break;
370 | }
371 | }
372 |
373 | function payloadOneTurn(payload, hex, dn) {
374 | dn = dn == null ? "float'" + hex.substring(2) + "'" : dn;
375 | let cbor = CBOR.NonFinite.createPayload(payload).encode();
376 | let object = CBOR.decode(cbor);
377 | assertTrue("plo1", object instanceof CBOR.NonFinite);
378 | let nonFinite = object;
379 | assertTrue("plo2", nonFinite.getPayload() == payload);
380 | assertTrue("plo3", CBOR.toHex(cbor) == hex);
381 | assertTrue("plo4", nonFinite.toString() == dn);
382 | assertTrue("plo5", nonFinite.getNonFinite() == CBOR.toBigInt(CBOR.fromHex(hex.substring(2), 16)));
383 | assertFalse("plo6", nonFinite.getSign());
384 | let signedHex = hex.substring(0, 2) + "f" +hex.substring(3);
385 | nonFinite.setSign(true);
386 | assertTrue("plo7", nonFinite.getSign());
387 | assertTrue("plo8", CBOR.toHex(nonFinite.encode()) == signedHex);
388 | nonFinite = CBOR.NonFinite.createPayload(payload).setSign(true);
389 | assertTrue("plo9", CBOR.toHex(nonFinite.encode()) == signedHex);
390 | }
391 |
392 | function oneNonFiniteTurn(value, binexpect, textexpect) {
393 | let nonfinite = CBOR.NonFinite(value);
394 | let text = nonfinite.toString();
395 | let returnValue = nonfinite.getNonFinite();
396 | let returnValue64 = nonfinite.getNonFinite64();
397 | let textdecode = CBOR.diagDecode(textexpect);
398 | let cbor = nonfinite.encode();
399 | let refcbor = CBOR.fromHex(binexpect);
400 | let hexbin = CBOR.toHex(cbor);
401 | assertTrue("eq1", text == textexpect);
402 | assertTrue("eq2", hexbin == binexpect);
403 | assertTrue("eq3", returnValue == CBOR.decode(cbor).getNonFinite());
404 | assertTrue("eq4", returnValue == textdecode.getNonFinite());
405 | assertTrue("eq5", CBOR.fromBigInt(returnValue).length == nonfinite.length);
406 | assertTrue("eq7", CBOR.fromBigInt(returnValue64).length == 8);
407 | assertTrue("eq8", nonfinite.equals(CBOR.decode(cbor)));
408 | let rawcbor = CBOR.fromBigInt(value);
409 | rawcbor = CBOR.addArrays(new Uint8Array([0xf9 + (rawcbor.length >> 2)]), rawcbor);
410 | if (rawcbor.length > refcbor.length) {
411 | try {
412 | CBOR.decode(rawcbor);
413 | fail("d1");
414 | } catch(error) {
415 | assertTrue("d2", error.toString().includes("Non-deterministic"));
416 | }
417 | } else {
418 | CBOR.decode(rawcbor);
419 | }
420 | assertTrue("d3", CBOR.initDecoder(rawcbor, CBOR.LENIENT_NUMBER_DECODING)
421 | .decodeWithOptions().equals(nonfinite));
422 | let object = CBOR.decode(refcbor);
423 | if (textexpect.includes("NaN") || textexpect.includes("Infinity")) {
424 | assertTrue("d4", object.getExtendedFloat64().toString() == textexpect);
425 | assertTrue("d5", object.isSimple());
426 | assertTrue("d6", textexpect.includes("Infinity") ^ object.isNaN());
427 | } else {
428 | try {
429 | object.getExtendedFloat64();
430 | fail("d7");
431 | } catch (error) {
432 | assertTrue("d8", error.toString().includes("7e00"));
433 | }
434 | assertFalse("d9", object.isSimple());
435 | }
436 | }
437 |
438 | oneTurn("0.0", "f90000");
439 | oneTurn("-0.0", "f98000");
440 | oneTurn("NaN", "f97e00");
441 | oneTurn("Infinity", "f97c00");
442 | oneTurn("-Infinity", "f9fc00");
443 | oneTurn("0.0000610649585723877", "fa38801000");
444 | oneTurn("10.559998512268066", "fa4128f5c1");
445 | oneTurn("65472.0", "f97bfe");
446 | oneTurn("65472.00390625", "fa477fc001");
447 | oneTurn("65503.0", "fa477fdf00");
448 | oneTurn("65504.0", "f97bff");
449 | oneTurn("65504.00000000001", "fb40effc0000000001");
450 | oneTurn("65504.00390625", "fa477fe001");
451 | oneTurn("65504.5", "fa477fe080");
452 | oneTurn("65505.0", "fa477fe100");
453 | oneTurn("131008.0", "fa47ffe000");
454 | oneTurn("-5.960464477539062e-8", "fbbe6fffffffffffff");
455 | oneTurn("-5.960464477539063e-8", "f98001");
456 | oneTurn("-5.960464477539064e-8", "fbbe70000000000001");
457 | oneTurn("-5.960465188081798e-8", "fab3800001");
458 | oneTurn("-5.963374860584736e-8", "fab3801000");
459 | oneTurn("-5.966285243630409e-8", "fab3802000");
460 | oneTurn("-8.940696716308594e-8", "fab3c00000");
461 | oneTurn("-0.00006097555160522461", "f983ff");
462 | oneTurn("-0.000060975551605224616", "fbbf0ff80000000001");
463 | oneTurn("-0.000060975555243203416", "fab87fc001");
464 | oneTurn("0.00006103515625", "f90400");
465 | oneTurn("0.00006103515625005551", "fb3f10000000001000");
466 | oneTurn("1.4012984643248169e-45", "fb369fffffffffffff");
467 | oneTurn("1.401298464324817e-45", "fa00000001");
468 | oneTurn("1.4012984643248174e-45", "fb36a0000000000001");
469 | oneTurn("1.4012986313726115e-45", "fb36a0000020000000");
470 | oneTurn("1.1754942106924411e-38", "fa007fffff");
471 | oneTurn("3.4028234663852886e+38", "fa7f7fffff");
472 | oneTurn("3.402823466385289e+38", "fb47efffffe0000001");
473 | oneTurn("0.00006109476089477539", "f90401");
474 | oneTurn("7.52316384526264e-37", "fa03800000");
475 | oneTurn("1.1754943508222875e-38", "fa00800000");
476 | oneTurn("5.0e-324", "fb0000000000000001");
477 | oneTurn("-1.7976931348623157e+308", "fbffefffffffffffff");
478 |
479 | oneNonFiniteTurn(0x7e00n, "f97e00", "NaN");
480 | oneNonFiniteTurn(0x7c01n, "f97c01", "float'7c01'");
481 | oneNonFiniteTurn(0xfc01n, "f9fc01", "float'fc01'");
482 | oneNonFiniteTurn(0x7fffn, "f97fff", "float'7fff'");
483 | oneNonFiniteTurn(0xfe00n, "f9fe00", "float'fe00'");
484 | oneNonFiniteTurn(0x7c00n, "f97c00", "Infinity");
485 | oneNonFiniteTurn(0xfc00n, "f9fc00", "-Infinity");
486 |
487 | oneNonFiniteTurn(0x7fc00000n, "f97e00", "NaN");
488 | oneNonFiniteTurn(0x7f800001n, "fa7f800001", "float'7f800001'");
489 | oneNonFiniteTurn(0xff800001n, "faff800001", "float'ff800001'");
490 | oneNonFiniteTurn(0x7fffffffn, "fa7fffffff", "float'7fffffff'");
491 | oneNonFiniteTurn(0xffc00000n, "f9fe00", "float'fe00'");
492 | oneNonFiniteTurn(0x7f800000n, "f97c00", "Infinity");
493 | oneNonFiniteTurn(0xff800000n, "f9fc00", "-Infinity");
494 |
495 | oneNonFiniteTurn(0x7ff8000000000000n, "f97e00", "NaN");
496 | oneNonFiniteTurn(0x7ff0000000000001n, "fb7ff0000000000001", "float'7ff0000000000001'");
497 | oneNonFiniteTurn(0xfff0000000000001n, "fbfff0000000000001", "float'fff0000000000001'");
498 | oneNonFiniteTurn(0x7fffffffffffffffn, "fb7fffffffffffffff", "float'7fffffffffffffff'");
499 | oneNonFiniteTurn(0x7ff0000020000000n, "fa7f800001", "float'7f800001'");
500 | oneNonFiniteTurn(0xfff0000020000000n, "faff800001", "float'ff800001'");
501 | oneNonFiniteTurn(0xfff8000000000000n, "f9fe00", "float'fe00'");
502 | oneNonFiniteTurn(0x7ff0040000000000n, "f97c01", "float'7c01'");
503 | oneNonFiniteTurn(0x7ff0000000000000n, "f97c00", "Infinity");
504 | oneNonFiniteTurn(0xfff0000000000000n, "f9fc00", "-Infinity");
505 |
506 | // Very special, some platforms natively support NaN with payloads, but we don't care
507 | // "signaling" NaN
508 | try {
509 | let nanWithPayload = new Uint8Array([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
510 | CBOR.Float.createExtendedFloat(new DataView(nanWithPayload.buffer, 0, 8).getFloat64(0, false));
511 | assertFalse("must not", supportNanWithPayloads);
512 | } catch (error) {
513 | assertTrue("not", error.toString().includes("payloads"));
514 | }
515 | let nonFinite = CBOR.Float.createExtendedFloat(Number.NaN);
516 | assertTrue("conv", nonFinite instanceof CBOR.NonFinite);
517 | assertTrue("truncated", nonFinite.getNonFinite64() == 0x7ff8000000000000n); // Returns "quiet" NaN
518 | assertTrue("cbor", CBOR.toHex(nonFinite.encode()) == "f97e00"); // Encoded as it should
519 | assertTrue("combined", Number.isNaN(nonFinite.getExtendedFloat64())); // Returns "Number"
520 | assertTrue("nan", nonFinite.isNaN(false)); // Indeed it is
521 |
522 | payloadOneTurn(0n, "f97c00", "Infinity");
523 | payloadOneTurn(1n, "f97e00", "NaN");
524 | payloadOneTurn(2n, "f97d00", null);
525 | payloadOneTurn((1n << 10n) - 1n, "f97fff", null);
526 | payloadOneTurn(1n << 10n, "fa7f801000", null);
527 | payloadOneTurn((1n << 23n) - 1n, "fa7fffffff", null);
528 | payloadOneTurn(1n << 23n, "fb7ff0000010000000", null);
529 | payloadOneTurn((1n << 52n) - 1n, "fb7fffffffffffffff", null);
530 |
531 | try {
532 | CBOR.NonFinite.createPayload(1n << 52n).encode();
533 | fail("pl8");
534 | } catch(error) {
535 | assertTrue("p18a", error.toString().includes("Payload out of range"));
536 | }
537 |
538 | success();
539 | `}
540 | ,
541 | {name:'hex.js',
542 | file:String.raw`// Test of "hex" utility methods
543 |
544 | const hex = '0123456789abcdefABCDEF';
545 |
546 | let bin = CBOR.fromHex(hex);
547 | let cnv = CBOR.toHex(bin);
548 | assertFalse("hex", CBOR.compareArrays(bin, CBOR.fromHex(cnv)));
549 | let ref = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef]);
550 | assertFalse("bin", CBOR.compareArrays(bin, ref));
551 | try {
552 | CBOR.fromHex("AAA");
553 | throw Error("should not");
554 | } catch (error) {
555 | if (!error.toString().includes("Unev")) {
556 | console.log(error);
557 | }
558 | }
559 |
560 | try {
561 | CBOR.fromHex("Ag");
562 | throw Error("should not");
563 | } catch (error) {
564 | if (!error.toString().includes("Bad hex")) {
565 | console.log(error);
566 | }
567 | }
568 | // Zero hex is accepted as well...
569 | assertFalse("zero", CBOR.compareArrays(CBOR.fromHex(''), new Uint8Array()));
570 | success();
571 | `}
572 | ,
573 | {name:'integer.js',
574 | file:String.raw`// Test program for integer "edge cases"
575 |
576 | function oneTurn(value, expected) {
577 | let text = value.toString();
578 | while (text.length < 25) {
579 | text += ' ';
580 | }
581 | let cbor = CBOR.BigInt(value).encode();
582 | let got = CBOR.toHex(cbor);
583 | if (got != expected) {
584 | got = '***=' + got;
585 | } else {
586 | got = '';
587 | }
588 | assertTrue("Failed decoding: " + value, CBOR.decode(cbor).getBigInt() == value);
589 | while (expected.length < 20) {
590 | expected += ' ';
591 | }
592 | if (got.length) {
593 | fail(text + expected + got);
594 | }
595 | }
596 | // -0 is treated as 0 for integers
597 | assertTrue("minus-0", CBOR.toHex(CBOR.Int(-0).encode()) == "00");
598 | oneTurn(0n, '00');
599 | oneTurn(-1n, '20');
600 | oneTurn(255n, '18ff');
601 | oneTurn(256n, '190100');
602 | oneTurn(-256n, '38ff');
603 | oneTurn(-257n, '390100');
604 | oneTurn(1099511627775n, '1b000000ffffffffff');
605 | oneTurn(18446744073709551615n, '1bffffffffffffffff');
606 | oneTurn(18446744073709551616n, 'c249010000000000000000');
607 | oneTurn(-18446744073709551616n, '3bffffffffffffffff');
608 | oneTurn(-18446744073709551617n, 'c349010000000000000000');
609 |
610 | try {
611 | CBOR.Int(1.1);
612 | fail("Should not");
613 | } catch (error) {
614 | assertTrue("msg1", error.toString().includes("Invalid integer: 1.1"));
615 | }
616 | try {
617 | CBOR.Int(Number.MAX_SAFE_INTEGER + 1);
618 | fail("Should not");
619 | } catch (error) {
620 | assertTrue("msg1", error.toString().includes("Invalid integer: " + (Number.MAX_SAFE_INTEGER + 1)));
621 | }
622 | try {
623 | CBOR.Int("10");
624 | fail("Should not");
625 | } catch (error) {
626 | assertTrue("msg2", error.toString().includes("Argument is not a 'number'"));
627 | }
628 | try {
629 | CBOR.BigInt("10");
630 | fail("Should not");
631 | } catch (error) {
632 | assertTrue("msg3", error.toString().includes("Argument is not a 'bigint'"));
633 | }
634 | try {
635 | CBOR.BigInt(1n, 7);
636 | fail("Should not");
637 | } catch (error) {
638 | assertTrue("msg4", error.toString().includes("CBOR.BigInt expects 1 argument(s)"));
639 | }
640 | try {
641 | CBOR.Int(1, 7);
642 | fail("Should not");
643 | } catch (error) {
644 | assertTrue("msg4", error.toString().includes("CBOR.Int expects 1 argument(s)"));
645 | }
646 |
647 | success();
648 | `}
649 | ,
650 | {name:'int-ranges.js',
651 | file:String.raw`// Testing range-constrained integers
652 |
653 | function goodRun(method, value) {
654 | let bigFlag = method.indexOf("64") > 0;
655 | let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
656 | let test = 'assertTrue("good", wrapper.' + method + '() == ' + (bigFlag ? value + 'n' : Number(value)) + ')';
657 | eval(test);
658 | }
659 |
660 | function badRun(method, value) {
661 | let wrapper = CBOR.decode(CBOR.BigInt(value).encode());
662 | let test = 'wrapper.' + method + '()';
663 | try {
664 | eval(test);
665 | assertTrue("Should fail", false);
666 | } catch (error) {
667 | if (!error.toString().includes('Value out of range:') &&
668 | !error.toString().includes('Number.MAX_SAFE_INTEGER')) {
669 | throw error;
670 | }
671 | }
672 | }
673 |
674 | function innerTurn(method, signed, size) {
675 | let min = signed ? -(1n << BigInt(size) - 1n) : 0n;
676 | let max = signed ? (1n << BigInt(size) - 1n) - 1n : (1n << BigInt(size)) - 1n;
677 | if (size == 53) {
678 | max = BigInt(Number.MAX_SAFE_INTEGER);
679 | min = -max;
680 | }
681 | goodRun(method, min);
682 | goodRun(method, max);
683 | goodRun(method, 10n);
684 | badRun(method, max + 1n);
685 | badRun(method, min - 1n);
686 | }
687 |
688 | function oneTurn(size) {
689 | innerTurn("getInt" + size, true, size);
690 | if (size != 53) {
691 | innerTurn("getUint" + size, false, size);
692 | }
693 | }
694 |
695 | oneTurn(8);
696 | oneTurn(16);
697 | oneTurn(32);
698 | oneTurn(53);
699 | oneTurn(64);
700 |
701 | success();
702 | `}
703 | ,
704 | {name:'maps.js',
705 | file:String.raw`// Testing map operations
706 |
707 | let map = CBOR.Map()
708 | .set(CBOR.Int(3), CBOR.String("three"))
709 | .set(CBOR.Int(4), CBOR.String("four"));
710 | assertTrue("size-0", map.length == 2);
711 | let keys = map.getKeys();
712 | assertTrue("size-1", keys.length == 2);
713 | assertTrue("get-0", map.get(keys[0]).getString() == "three");
714 | assertTrue("get-1", map.get(keys[1]).getString() == "four");
715 |
716 | assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
717 | assertTrue("size-2", map.length == 1);
718 | assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
719 | assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
720 | assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
721 | assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
722 | map = map.merge(
723 | CBOR.Map().set(CBOR.Int(1), CBOR.String("hi")).set(CBOR.Int(5), CBOR.String("yeah")));
724 | assertTrue("size-3", map.length == 3);
725 | assertTrue("merge-0", map.get(CBOR.Int(1)).getString() == "hi");
726 | assertTrue("upd-0", map.update(CBOR.Int(1), CBOR.BigInt(-8n), true).getString() == "hi");
727 | assertTrue("upd-1", map.get(CBOR.Int(1)).getBigInt() == -8n);
728 | assertTrue("upd-2", map.update(CBOR.Int(10), CBOR.BigInt(-8n), false) == null);
729 | assertTrue("upd-3", map.get(CBOR.Int(10)).getBigInt() == -8n);
730 |
731 | function badKey(js) {
732 | try {
733 | eval(js);
734 | fail("Must fail!");
735 | } catch (error) {
736 | if (!error.toString().includes('Map key')) {
737 | throw error;
738 | }
739 | }
740 | }
741 |
742 | let immutableKey1 = CBOR.Array();
743 | let immutableKey2 = CBOR.Array();
744 | CBOR.Map().set(immutableKey1, CBOR.Int(4));
745 | badKey("immutableKey1.add(CBOR.Int(6))");
746 | let mutableValue = CBOR.Array();
747 | CBOR.Map().set(CBOR.Int(5), mutableValue);
748 | mutableValue.add(CBOR.Map());
749 | CBOR.Map().set(CBOR.Array().add(immutableKey2), CBOR.Int(5));
750 | badKey("immutableKey2.add(CBOR.Int(6))");
751 |
752 | success();
753 | `}
754 | ,
755 | {name:'arrays.js',
756 | file:String.raw`// Testing array operations
757 |
758 | let array = CBOR.Array()
759 | .add(CBOR.String("three"))
760 | .add(CBOR.String("four"));
761 | assertTrue("size-0", array.length == 2);
762 | assertTrue("get-0", array.get(0).getString() == "three");
763 | assertTrue("get-1", array.get(1).getString() == "four");
764 | let arrayElements = array.toArray();
765 | assertTrue("size-1", arrayElements.length == 2);
766 | assertTrue("arr-0", arrayElements[0].getString() == "three");
767 | assertTrue("arr-1", arrayElements[1].getString() == "four");
768 | assertTrue("upd-1", array.update(1, CBOR.Int(1)).getString() == "four");
769 | assertTrue("upd-2", array.get(1).getInt8() == 1);
770 | assertTrue("size-1", array.length == 2);
771 | assertTrue("upd-3", array.get(0).getString() == "three");
772 | assertTrue("upd-4", array.insert(array.length, CBOR.Int(-8)) == array);
773 | assertTrue("upd-5", array.get(array.length - 1).equals(CBOR.Int(-8)));
774 | assertTrue("upd-4", array.insert(0, CBOR.Int(-9)) == array);
775 | assertTrue("upd-5", array.get(0).equals(CBOR.Int(-9)));
776 | let l = array.length;
777 | assertTrue("upd-6", array.remove(0).equals(CBOR.Int(-9)));
778 | assertTrue("upd-7", l == array.length + 1);
779 | assertTrue("upd-8", array.get(0).getString() == "three");
780 | assertTrue("upd-9", array.toDiag(false) == '["three",1,-8]');
781 |
782 | function aBadOne(expression) {
783 | try {
784 | eval("array." + expression);
785 | fail("Should not pass");
786 | } catch (Error) {
787 | }
788 | }
789 |
790 | aBadOne("get('string')")
791 | aBadOne("get(array.length)");
792 | aBadOne("get(-1)");
793 | aBadOne("insert(array.length + 1, CBOR.Int(6))");
794 | aBadOne("insert(array.length)");
795 | aBadOne("remove(array.length)");
796 | aBadOne("remove(array.length - 1, 'hi')");
797 | aBadOne("get(0, 6)");
798 |
799 | /*
800 | assertTrue("rem-0", map.remove(CBOR.Int(4)).getString() == "four");
801 | assertTrue("size-2", map.length == 1);
802 | assertTrue("avail-0", map.containsKey(CBOR.Int(3)));
803 | assertFalse("avail-1", map.containsKey(CBOR.Int(4)));
804 | assertTrue("cond-0", map.getConditionally(CBOR.Int(3), CBOR.String("k3")).getString() == "three");
805 | assertTrue("cond-1", map.getConditionally(CBOR.Int(4), CBOR.String("k4")).getString() == "k4");
806 | */
807 |
808 | success();
809 | `}
810 | ,
811 | {name:'miscellaneous.js',
812 | file:String.raw`// miscellaneous tests
813 |
814 | let bin = new Uint8Array([0xa5, 0x01, 0xd9, 0x01, 0xf4, 0x81, 0x18, 0x2d, 0x02, 0xf9, 0x80, 0x10,
815 | 0x04, 0x64, 0x53, 0x75, 0x72, 0x65, 0x05, 0xa2, 0x08, 0x69, 0x59, 0x65,
816 | 0x0a, 0x01, 0x61, 0x68, 0xe2, 0x82, 0xac, 0x09, 0x85, 0x66, 0x42, 0x79,
817 | 0x74, 0x65, 0x73, 0x21, 0x45, 0x01, 0x02, 0x03, 0x04, 0x05, 0xf5, 0xf4,
818 | 0xf6, 0x06, 0xc2, 0x4b, 0x66, 0x1e, 0xfd, 0xf2, 0xe3, 0xb1, 0x9f, 0x7c,
819 | 0x04, 0x5f, 0x15]);
820 |
821 | let cbor = CBOR.Map()
822 | .set(CBOR.Int(5),
823 | CBOR.Map()
824 | .set(CBOR.Int(8), CBOR.String("Ye\n\u0001ah€"))
825 | .set(CBOR.Int(9),
826 | CBOR.Array()
827 | .add(CBOR.String("Bytes!"))
828 | .add(CBOR.Bytes(new Uint8Array([1,2,3,4,5])))
829 | .add(CBOR.Boolean(true))
830 | .add(CBOR.Boolean(false))
831 | .add(CBOR.Null())))
832 | .set(CBOR.Int(4), CBOR.String("Sure"))
833 | .set(CBOR.Int(2), CBOR.Float(-9.5367431640625e-7))
834 | .set(CBOR.Int(6), CBOR.BigInt(123456789123456789123456789n))
835 | .set(CBOR.Int(1), CBOR.Tag(500n, CBOR.Array().add(CBOR.Int(45)))).encode();
836 | assertFalse("cmp1", CBOR.compareArrays(bin, cbor));
837 | let array = CBOR.decode(cbor).get(CBOR.Int(5)).get(CBOR.Int(9));
838 | assertTrue("bool1", array.get(2).getBoolean());
839 | assertFalse("bool1", array.get(3).getBoolean());
840 | assertFalse("null1", array.get(3).isNull());
841 | assertTrue("null2", array.get(4).isNull());
842 | assertFalse("cmp2", CBOR.compareArrays(CBOR.diagDecode(CBOR.decode(cbor).toString()).encode(), bin));
843 |
844 | assertTrue("version", CBOR.version == "1.0.16");
845 |
846 | success();
847 | `}
848 | ,
849 | {name:'nondeterministic.js',
850 | file:String.raw`// Testing "deterministic" code checks
851 |
852 | function oneTurn(hex, dn) {
853 | try {
854 | CBOR.decode(CBOR.fromHex(hex));
855 | throw Error("Should not fail on: " + dn);
856 | } catch (error) {
857 | if (!error.toString().includes("Non-d")) {
858 | throw error;
859 | }
860 | }
861 | let object = CBOR.initDecoder(CBOR.fromHex(hex),
862 | dn.includes("{") ? CBOR.LENIENT_MAP_DECODING : CBOR.LENIENT_NUMBER_DECODING).decodeWithOptions();
863 | if (object.toString() != dn || !object.equals(CBOR.decode(object.encode()))) {
864 | throw Error("non match:" + dn);
865 | }
866 | }
867 |
868 | oneTurn('1900ff', '255');
869 | oneTurn('1817', '23');
870 | oneTurn('A2026374776F01636F6E65', '{\n 1: "one",\n 2: "two"\n}');
871 | oneTurn('FB7FF8000000000000', 'NaN');
872 | oneTurn('FA7FC00000', 'NaN');
873 | oneTurn('FB3ff0000000000000', '1.0');
874 | oneTurn('c2480100000000000000', '72057594037927936');
875 | oneTurn('c24900ffffffffffffffff', '18446744073709551615');
876 | oneTurn('c240', '0');
877 |
878 | // This one is actually deterministic...
879 | try {
880 | oneTurn('fa7f7fffff', '3.4028234663852886e+38');
881 | } catch (error) {
882 | if (!error.toString().includes('Should not')) {
883 | throw error;
884 | }
885 | }
886 |
887 | success();
888 | `}
889 | ,
890 | {name:'out-of-range.js',
891 | file:String.raw`// Number overflow tests.
892 |
893 | const TOO_BIG = Number.MAX_SAFE_INTEGER + 1;
894 | const IN_RANGE = Number.MAX_SAFE_INTEGER;
895 |
896 | try {
897 | CBOR.Int(TOO_BIG);
898 | throw Error('Should not');
899 | } catch (error) {
900 | if (error.toString().includes('Should not')) {
901 | throw error;
902 | }
903 | }
904 | let cbor = CBOR.BigInt(BigInt(TOO_BIG)).encode();
905 | try {
906 | CBOR.decode(cbor).getInt53();
907 | throw Error('Should not');
908 | } catch (error) {
909 | if (error.toString().includes('Should not')) {
910 | throw error;
911 | }
912 | }
913 | assertTrue("big", BigInt(TOO_BIG) == CBOR.decode(cbor).getBigInt());
914 |
915 | cbor = CBOR.Int(IN_RANGE).encode();
916 | assertTrue("R0", CBOR.decode(cbor).getInt53() == IN_RANGE);
917 | cbor = CBOR.Int(-IN_RANGE).encode();
918 | assertTrue("R0", CBOR.decode(cbor).getInt53() == -IN_RANGE);
919 |
920 | success();
921 | `}
922 | ,
923 | {name:'sequence.js',
924 | file:String.raw`// Testing the "sequence" option
925 |
926 | let cbor = new Uint8Array([0x05, 0xa1, 0x05, 0x42, 0x6a, 0x6a])
927 | try {
928 | CBOR.decode(cbor);
929 | throw Error("Should not");
930 | } catch (error) {
931 | if (!error.toString().includes('Unexpected')) console.log(error);
932 | }
933 | let decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
934 | let total = new Uint8Array();
935 | let object;
936 | while (object = decoder.decodeWithOptions()) {
937 | total = CBOR.addArrays(total, object.encode());
938 | }
939 | assertFalse("Comp", CBOR.compareArrays(total, cbor));
940 | assertTrue("Comp2", total.length == decoder.getByteCount());
941 | decoder = CBOR.initDecoder(new Uint8Array(), CBOR.SEQUENCE_MODE);
942 | assertFalse("Comp3", decoder.decodeWithOptions());
943 | assertTrue("Comp4", decoder.getByteCount() == 0);
944 | let arraySequence = CBOR.Array();
945 | decoder = CBOR.initDecoder(cbor, CBOR.SEQUENCE_MODE);
946 | while (object = decoder.decodeWithOptions()) {
947 | arraySequence.add(object);
948 | }
949 | assertFalse("Comp5", CBOR.compareArrays(arraySequence.encodeAsSequence(), cbor));
950 |
951 | success();
952 | `}
953 | ,
954 | {name:'tags.js',
955 | file:String.raw`// Testing "tag"
956 |
957 | let object = CBOR.Array().add(CBOR.String("https://example.com/myobject")).add(CBOR.Int(6));
958 | let cbor = CBOR.Tag(CBOR.Tag.TAG_COTX, object).encode();
959 | let tag = CBOR.decode(cbor);
960 | assertTrue("t3", tag.getTagNumber()== CBOR.Tag.TAG_COTX);
961 | assertTrue("t3.1", object.equals(tag.get()));
962 | assertTrue("t3.2", tag.cotxId == "https://example.com/myobject");
963 | assertTrue("t3.3", tag.cotxObject.equals(CBOR.Int(6)));
964 | cbor = CBOR.Tag(0xf0123456789abcden, object).encode();
965 | assertTrue("t14", CBOR.decode(cbor).getTagNumber()== 0xf0123456789abcden);
966 | assertTrue("t5", CBOR.toHex(cbor) ==
967 | "dbf0123456789abcde82781c68747470733a2f2f6578616d706c652e636f6d2f6d796f626a65637406");
968 |
969 | [-1n, 0x10000000000000000n].forEach(tagNumber => {
970 | try {
971 | CBOR.Tag(tagNumber, CBOR.String("any"));
972 | throw Error("Should not");
973 | } catch (error) {
974 | if (!error.toString().includes("out of range")) {
975 | throw error;
976 | }
977 | }
978 | });
979 |
980 | [2n, 3n].forEach(tagNumber => {
981 | try {
982 | CBOR.Tag(tagNumber, CBOR.String("any"));
983 | throw Error("Should not");
984 | } catch (error) {
985 | if (!error.toString().includes("'bigint'")) {
986 | throw error;
987 | }
988 | }
989 | });
990 |
991 | [0n, 1n].forEach(tagNumber => {
992 | try {
993 | CBOR.Tag(tagNumber, CBOR.Boolean(true));
994 | throw Error("Should not");
995 | } catch (error) {
996 | if (!error.toString().includes("got: CBOR.Boolean")) {
997 | throw error;
998 | }
999 | }
1000 | });
1001 |
1002 | success();
1003 | `}
1004 | ,
1005 | {name:'simple.js',
1006 | file:String.raw`// Testing "simple"
1007 |
1008 | [-1, 256, 24, 31].forEach(value => {
1009 | try {
1010 | CBOR.Simple(value);
1011 | throw Error("Should not");
1012 | } catch (error) {
1013 | if (!error.toString().includes("out of range")) {
1014 | throw error;
1015 | }
1016 | }
1017 | });
1018 |
1019 | function oneTurn(value, hex) {
1020 | let s = CBOR.Simple(value);
1021 | let s2 = CBOR.decode(s.encode());
1022 | assertTrue("v", s.getSimple() == value);
1023 | assertTrue("v2", s2.getSimple() == value);
1024 | assertTrue("b", CBOR.toHex(s2.encode()) == hex);
1025 | }
1026 |
1027 | oneTurn(0, "e0");
1028 | oneTurn(23, "f7");
1029 | oneTurn(32, "f820");
1030 | oneTurn(255, "f8ff");
1031 |
1032 | success();
1033 | `}
1034 | ,
1035 | {name:'dates.js',
1036 | file:String.raw`// Testing instant methods
1037 |
1038 | function oneGetDateTime(epoch, isoString) {
1039 | assertTrue("date1", CBOR.String(isoString).getDateTime().getTime() == epoch);
1040 | let cbor = CBOR.decode(CBOR.String(isoString).encode());
1041 | assertTrue("date2", cbor.getDateTime().getTime() == epoch);
1042 | assertTrue("date3", CBOR.Tag(0n, CBOR.String(isoString)).getDateTime().getTime() == epoch);
1043 | }
1044 |
1045 | function badDate(hexBor, err) {
1046 | try {
1047 | CBOR.decode(CBOR.fromHex(hexBor));
1048 | fail("must not");
1049 | } catch (error) {
1050 | if (!error.toString().includes(err)) {
1051 | throw error;
1052 | }
1053 | }
1054 | }
1055 |
1056 | function truncateDateTime(iso, millis, seconds) {
1057 | let dateTime = CBOR.String(iso).getDateTime();
1058 | assertTrue("trdt1", dateTime.getTime() == millis);
1059 | assertTrue("trdt2", CBOR.createDateTime(dateTime, true, false).getDateTime().getTime() == millis);
1060 | assertTrue("trdt3", CBOR.createDateTime(dateTime, false, false).getDateTime().getTime() == seconds * 1000);
1061 | }
1062 |
1063 | function truncateEpochTime(float, millis, seconds) {
1064 | let epoch = CBOR.Float(float).getEpochTime();
1065 | assertTrue("tr1", epoch.getTime() == millis);
1066 | assertTrue("tr2", CBOR.createEpochTime(epoch, true).getEpochTime().getTime() == millis);
1067 | assertTrue("tr3", CBOR.createEpochTime(epoch, false).getEpochTime().getTime() == seconds * 1000);
1068 | }
1069 |
1070 | function oneGetEpochTime(hexBor, epoch, err) {
1071 | let time = Math.floor((epoch * 1000) + 0.5);
1072 | let instant = CBOR.decode(CBOR.fromHex(hexBor)).getEpochTime();
1073 | assertTrue("epoch1", instant.getTime() == time);
1074 | let cborObject = CBOR.createEpochTime(instant, time % 1000);
1075 | // console.log("E=" + cborObject.toString());
1076 | // console.log("1=" + cborObject.getEpochTime().getTime() + " 2=" + time);
1077 | assertTrue("epoch2", cborObject.getEpochTime().getTime() == time);
1078 | if (time % 1000 > 500) {
1079 | let p1 = Math.floor(epoch + 1.0) * 1000;
1080 | cborObject = CBOR.createEpochTime(instant, false);
1081 | // console.log("r1=" + cborObject.getEpochTime().getTime() + " r2=" + p1);
1082 | assertTrue("epoch3", cborObject.getEpochTime().getTime() == p1);
1083 | }
1084 | instant = CBOR.decode(CBOR.fromHex(hexBor));
1085 | try {
1086 | instant.checkForUnread();
1087 | fail("must not");
1088 | } catch (error) {
1089 | if (!error.toString().includes(err)) {
1090 | throw error;
1091 | }
1092 | }
1093 | instant.getEpochTime();
1094 | instant.checkForUnread();
1095 | }
1096 |
1097 | oneGetDateTime(1740060548000, "2025-02-20T14:09:08+00:00");
1098 | oneGetDateTime(1740060548000, "2025-02-20T14:09:08Z");
1099 | oneGetDateTime(1740060548000, "2025-02-20T15:09:08+01:00");
1100 | oneGetDateTime(1740060548000, "2025-02-20T15:39:08+01:30");
1101 | oneGetDateTime(1740060548000, "2025-02-20T12:09:08-02:00");
1102 | oneGetDateTime(1740060548000, "2025-02-20T11:39:08-02:30");
1103 | oneGetDateTime(1740060548123, "2025-02-20T11:39:08.123-02:30");
1104 | oneGetDateTime(1740060548930, "2025-02-20T14:09:08.930Z");
1105 | // Next: Truncates!
1106 | oneGetDateTime(1740060548930, "2025-02-20T14:09:08.9305Z");
1107 | oneGetDateTime(-62167219200000, "0000-01-01T00:00:00Z");
1108 | oneGetDateTime(253402300799000, "9999-12-31T23:59:59Z");
1109 |
1110 | badDate("c001", "got: CBOR.Int");
1111 | badDate("c06135", "Invalid ISO date string: 5");
1112 | badDate("c16135", "got: CBOR.String");
1113 |
1114 | oneGetEpochTime("1A67B73784", 1740060548, "Data of type=CBOR.Int");
1115 | oneGetEpochTime("FB41D9EDCDE113645A", 1740060548.303, "Data of type=CBOR.Float with value=174");
1116 | oneGetEpochTime("c1FB41D9EDCDE113645A", 1740060548.303, "Tagged object 1 of type=CBOR.Float");
1117 | // Next: Truncates!
1118 | // oneGetEpochTime("c1fb41d9edcde1136c8b", 1740060548.3035, "Tagged object 1 of type=CBOR.Float");
1119 | // oneGetEpochTime("c1fb41d9edcde1204189", 1740060548.5045, "Tagged object 1 of type=CBOR.Float");
1120 | oneGetEpochTime("c11b0000003afff4417f", 253402300799, "Tagged object 1 of type=CBOR.Int");
1121 | oneGetEpochTime("00", 0, "Data of type=CBOR.Int");
1122 |
1123 | function oneMillis(time, iso) {
1124 | let instant = new Date();
1125 | instant.setTime(time);
1126 | assertTrue("cdt1=", CBOR.createDateTime(instant, true, true).getString() == iso);
1127 | let created = CBOR.createDateTime(instant, true, false);
1128 | assertTrue("cdt2=", created.getDateTime().getTime() == time);
1129 | assertTrue("cdt3=", created.getString().length == iso.length + 5);
1130 | created = CBOR.createEpochTime(instant, true);
1131 | assertTrue("cet1=", created.getEpochTime().getTime() == time);
1132 | assertTrue("cet2=", created instanceof CBOR.Float == iso.includes("."));
1133 | }
1134 |
1135 | oneMillis(1752189147000, "2025-07-10T23:12:27Z");
1136 | oneMillis(1752189147123, "2025-07-10T23:12:27.123Z");
1137 | oneMillis(1752189147120, "2025-07-10T23:12:27.12Z");
1138 | oneMillis(1752189147100, "2025-07-10T23:12:27.1Z");
1139 |
1140 | truncateEpochTime(1740060548.000, 1740060548000, 1740060548);
1141 | truncateEpochTime(0.0, 0, 0);
1142 | truncateEpochTime(1740060548.0004, 1740060548000, 1740060548);
1143 | truncateEpochTime(1740060548.0005, 1740060548001, 1740060548);
1144 |
1145 | truncateDateTime("2025-07-10T23:12:27Z", 1752189147000, 1752189147);
1146 | truncateDateTime("2025-07-10T23:12:27.1Z", 1752189147100, 1752189147);
1147 | truncateDateTime("2025-07-10T23:12:27.12Z", 1752189147120, 1752189147);
1148 | truncateDateTime("2025-07-10T23:12:27.123Z", 1752189147123, 1752189147);
1149 | truncateDateTime("2025-07-10T23:12:27.1233Z", 1752189147123, 1752189147);
1150 | truncateDateTime("2025-07-10T23:12:27.1235Z", 1752189147123, 1752189147);
1151 | truncateDateTime("2025-07-10T23:12:27.523Z", 1752189147523, 1752189148);
1152 |
1153 | truncateDateTime("1925-07-10T23:12:27Z", -1403570853000, -1403570853);
1154 | truncateDateTime("1925-07-10T23:12:27.1Z", -1403570852900, -1403570853);
1155 | truncateDateTime("1925-07-10T23:12:27.12Z", -1403570852880, -1403570853);
1156 | truncateDateTime("1925-07-10T23:12:27.123Z", -1403570852877, -1403570853);
1157 | truncateDateTime("1925-07-10T23:12:27.1233Z", -1403570852877, -1403570853);
1158 | truncateDateTime("1925-07-10T23:12:27.1235Z", -1403570852877, -1403570853);
1159 | truncateDateTime("1925-07-10T23:12:27.499Z", -1403570852501, -1403570853);
1160 | truncateDateTime("1925-07-10T23:12:27.500Z", -1403570852500, -1403570852);
1161 | truncateDateTime("1925-07-10T23:12:27.700Z", -1403570852300, -1403570852);
1162 |
1163 | try {
1164 | // Z or -+local offset needed.
1165 | CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43"));
1166 | throw Error("Should not");
1167 | } catch (error) {
1168 | if (!error.toString().includes("ISO")) {
1169 | throw error;
1170 | }
1171 | }
1172 |
1173 | try {
1174 | // Beyond nano-seconds
1175 | CBOR.Tag(0n, CBOR.String("2023-06-22T00:01:43.6666666666Z"));
1176 | throw Error("Should not");
1177 | } catch (error) {
1178 | if (!error.toString().includes("ISO")) {
1179 | throw error;
1180 | }
1181 | }
1182 |
1183 | try {
1184 | // 24 hour is incorrect.
1185 | CBOR.Tag(0n, CBOR.String("2023-06-22T24:01:43Z"));
1186 | throw Error("Should not");
1187 | } catch (error) {
1188 | if (!error.toString().includes("ISO")) {
1189 | throw error;
1190 | }
1191 | }
1192 |
1193 | [-1, 253402300800].forEach(epoch => {
1194 | try {
1195 | // Out of range for Date().
1196 | CBOR.Tag(1n, CBOR.Int(epoch));
1197 | throw Error("Should not");
1198 | } catch (error) {
1199 | if (!error.toString().includes("Epoch out of")) {
1200 | throw error;
1201 | }
1202 | }
1203 | try {
1204 | // Out of range for Date().
1205 | let instant = new Date();
1206 | instant.setTime(epoch * 1000);
1207 | CBOR.createEpochTime(instant, true);
1208 | throw Error("Should not");
1209 | } catch (error) {
1210 | if (!error.toString().includes("Epoch out of")) {
1211 | throw error;
1212 | }
1213 | }
1214 | });
1215 |
1216 | assertTrue("zero", CBOR.String("0000-01-01T00:00:00Z").getDateTime().getTime() == -62167219200000);
1217 |
1218 | let now = new Date();
1219 | /*
1220 | console.log("Now=" + now.getTime() + " iso=" + now.toISOString() +
1221 | " offset=" + now.getTimezoneOffset() + " str=" + now.toString());
1222 | */
1223 |
1224 | function oneCreateDateTime(dateOrTime, utc, millis, bad) {
1225 | let instant = new Date();
1226 | let time = typeof dateOrTime == 'number' ? Math.round(dateOrTime) : dateOrTime.getTime();
1227 | instant.setTime(time);
1228 | if (bad) {
1229 | try {
1230 | CBOR.createDateTime(instant, millis, utc);
1231 | throw Error("Should not");
1232 | } catch (error) {
1233 | if (!error.toString().includes("Date object out of range")) {
1234 | throw error;
1235 | }
1236 | }
1237 | } else {
1238 | let dateTime = CBOR.createDateTime(instant, millis, utc);
1239 | if (millis || !(time % 1000)) {
1240 | assertTrue("cdt1" + dateTime, dateTime.getDateTime().getTime() == time);
1241 | } else if (!millis && time % 1000) {
1242 | assertFalse("cdt2" + dateTime, dateTime.getDateTime().getTime() == time);
1243 | }
1244 | }
1245 | }
1246 |
1247 | oneCreateDateTime(now, true, false);
1248 | oneCreateDateTime(now, true, true);
1249 | oneCreateDateTime(now, false, false);
1250 | oneCreateDateTime(now, false, true);
1251 |
1252 | oneCreateDateTime(1740060548000, true, false);
1253 | oneCreateDateTime(1740060548000, true, true);
1254 | oneCreateDateTime(1740060548501, true, false);
1255 | oneCreateDateTime(1740060548501, true, true);
1256 | oneCreateDateTime(1740060548000.3, true, true);
1257 | oneCreateDateTime(1740060548000.5, true, true);
1258 | oneCreateDateTime(-62167219200000, true, true);
1259 | oneCreateDateTime(-62167219200001, true, true, true);
1260 | oneCreateDateTime(253402300799000, true, true);
1261 | oneCreateDateTime(253402300799001, true, true, true);
1262 |
1263 | success();`}
1264 | ,
1265 | {name:'dynamic.js',
1266 | file:String.raw`// dynamic tests
1267 |
1268 | assertTrue("dyn", CBOR.Map().setDynamic(wr =>
1269 | wr.set(CBOR.Int(1), CBOR.Boolean(true))).get(CBOR.Int(1)).getBoolean());
1270 |
1271 | let option = "on";
1272 | assertTrue("dyn", CBOR.Map().setDynamic(wr => {
1273 | if (option) {
1274 | wr.set(CBOR.Int(1), CBOR.String(option));
1275 | }
1276 | return wr;
1277 | }).get(CBOR.Int(1)).getString() == option);
1278 |
1279 | function lambda(wr) {
1280 | wr.set(CBOR.Int(1), CBOR.Boolean(true));
1281 | return wr;
1282 | }
1283 | assertTrue("dyn", CBOR.Map().setDynamic(lambda).get(CBOR.Int(1)).getBoolean());
1284 |
1285 | success();
1286 | `}
1287 | ,
1288 | {name:'utf8.js',
1289 | file:String.raw`// Test of "utf8" converters
1290 |
1291 | function utf8EncoderTest(string, ok) {
1292 | try {
1293 | CBOR.String(string).encode();
1294 | assertTrue("enc", ok);
1295 | } catch (error) {
1296 | assertFalse("No good", ok);
1297 | }
1298 |
1299 | }
1300 |
1301 | function utf8DecoderTest(hex, ok) {
1302 | let cbor = CBOR.fromHex(hex);
1303 | let roundTrip;
1304 | try {
1305 | roundTrip = CBOR.decode(cbor).encode();
1306 | } catch (error) {
1307 | assertFalse("No good", ok);
1308 | return;
1309 | }
1310 | assertTrue("OK", ok);
1311 | assertFalse("Conv", CBOR.compareArrays(cbor, roundTrip));
1312 | }
1313 |
1314 | utf8DecoderTest("62c328", false);
1315 | utf8DecoderTest("64f0288cbc", false);
1316 | utf8DecoderTest("64f0908cbc", true);
1317 | utf8EncoderTest("Hi", true)
1318 | utf8EncoderTest("\uD83D", false);
1319 | utf8EncoderTest("\uD83D\uDE2D", true);
1320 |
1321 | success();
1322 | `}
1323 | ,
1324 | {name:'xyz-encoder.js',
1325 | file:String.raw`// Simple "encoder" API
1326 |
1327 | class XYZEncoder {
1328 |
1329 | static COUNTER = CBOR.Int(1);
1330 | static TEMPERATURE = CBOR.Int(2);
1331 | static GREETING = CBOR.Int(3);
1332 |
1333 | #map;
1334 |
1335 | constructor() {
1336 | this.#map = CBOR.Map();
1337 | }
1338 |
1339 | setCounter = function(intVal) {
1340 | this.#map.set(XYZEncoder.COUNTER, CBOR.Int(intVal));
1341 | return this;
1342 | }
1343 |
1344 | setTemperature = function(floatVal) {
1345 | this.#map.set(XYZEncoder.TEMPERATURE, CBOR.Float(floatVal));
1346 | return this;
1347 | }
1348 |
1349 | setGreeting = function(stringVal) {
1350 | this.#map.set(XYZEncoder.GREETING, CBOR.String(stringVal));
1351 | return this;
1352 | }
1353 |
1354 | encode = function() {
1355 | assertTrue("incomplete", this.#map.length == 3);
1356 | return this.#map.encode();
1357 | }
1358 | }
1359 |
1360 | let cbor = new XYZEncoder()
1361 | .setCounter(2)
1362 | .setGreeting('Hi!')
1363 | .setTemperature(53.0001)
1364 | .encode();
1365 |
1366 | assertTrue("bad code", CBOR.toHex(cbor) == 'a3010202fb404a800346dc5d640363486921');
1367 |
1368 | success();
1369 | `}
1370 | ,
1371 | {name:'xyz-decoder.js',
1372 | file:String.raw`// Simple "decoder" API
1373 |
1374 | class XYZDecoder {
1375 |
1376 | static COUNTER = CBOR.Int(1);
1377 | static TEMPERATURE = CBOR.Int(2);
1378 | static GREETING = CBOR.Int(3);
1379 |
1380 | #counter;
1381 | #temperature;
1382 | #greeting;
1383 |
1384 | constructor(cbor) {
1385 | // There MUST be exactly three key/value pairs.
1386 | // CBOR data items are type-checked as well.
1387 | let map = CBOR.decode(cbor);
1388 | // If the top-level object is not a CBOR map, the next
1389 | // JavaScript line will throw an exception because there is
1390 | // only one get-method that has a CBOR wrapper as input parameter.
1391 | this.#counter = map.get(XYZDecoder.COUNTER).getUint8();
1392 | this.#temperature = map.get(XYZDecoder.TEMPERATURE).getFloat64();
1393 | this.#greeting = map.get(XYZDecoder.GREETING).getString();
1394 | // We got more than we asked for?
1395 | map.checkForUnread();
1396 | }
1397 |
1398 | get counter() {
1399 | return this.#counter;
1400 | }
1401 |
1402 | get temperature() {
1403 | return this.#temperature;
1404 | }
1405 |
1406 | get greeting() {
1407 | return this.#greeting;
1408 | }
1409 |
1410 | }
1411 |
1412 | let cbor = CBOR.fromHex('a3010202fb404a800346dc5d640363486921');
1413 |
1414 | let xyz = new XYZDecoder(cbor);
1415 |
1416 | assertTrue("counter", xyz.counter == 2);
1417 | assertTrue("temperature", xyz.temperature == 53.0001);
1418 | assertTrue("greeting", xyz.greeting == 'Hi!');
1419 |
1420 | success();
1421 | `}
1422 |
1423 | ];
1424 |
1425 | function runTest() {
1426 | test = 0;
1427 | failures = 0;
1428 | for (let test = 0; test < TESTS.length; test++) {
1429 | name = TESTS[test].name;
1430 | try {
1431 | eval(TESTS[test].file);
1432 | } catch (error) {
1433 | failures++;
1434 | console.log(name + " FAILED: " + error);
1435 | }
1436 | }
1437 | if (failures) {
1438 | console.log('There were ' + failures + ' errors');
1439 | } else {
1440 | console.log('PASSED');
1441 | }
1442 | }
1443 |
1444 | runTest();
1445 |
--------------------------------------------------------------------------------