├── .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 |
![CBOR is great](https://cyberphone.github.io/CBOR.js/doc/cbor.js.svg) 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 | 21 | 22 | 23 | 24 | \n`: `
Diagnostic NotationCBOR EncodingComment
`; 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 ? '\n\n\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
' + 36 | numberText + 37 | '' + cborHex + 38 | '' + comment + '
\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 | logo 160 |
CBOR Playground
161 |
162 | CBOR.js 163 | supports the 164 | [CBOR::Corelink] primitives 166 | (tstr, bstr, int, 167 | bigint, float, 168 | bool, null, 169 | tagged data, and 170 | simple values). 171 |
172 |
173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 |
Diagnostic Notation
Hexadecimal notation (including possible #-comments)
0xhh, 0xhh... notation
Base64Url notation
Sequence. For Diagnostic Notation use comma (,) as separator
Require Deterministic Encoding for hex/b64u data
Reject NaN and Infinity floating-point objects
Paste a CBOR object in the text box or try with the default: 189 |
190 |
191 | 192 | 193 | 194 | 195 | 196 | 197 |
Diagnostic Notation
Hexadecimal notation
0xhh, 0xhh... notation
Base64Url notation
Processed result using Deterministic Encoding:
198 |
199 |
200 |
201 |
Process!
202 |
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 [RFC8259link], 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 | 24 | 26 | 35 | 40 |
LevelComment
BASICOnly "regular" floating-point numbers are accepted. 25 |
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 |
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 |
41 | Non-supported items cause a CborException to be thrown. 42 | Note that the constraints apply to encoding as well. 43 |
Encoding Methods
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
BASIC
CBOR.Float(value)// "Regular" number
EXTENDED
CBOR.Float.createExtendedFloat(value)// "Regular" number, NaN, or Infinity
COMPLETE
// Select suitable method:                    
CBOR.Float(value)// "Regular" number
CBOR.NonFinite(value)// Arbitrary non-finite number
CBOR.NonFinite.createPayload(payload)// Application-specific data
56 |
57 |
Decoding Methods
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
BASIC
cborObject.getFloat64()// "Regular" number
EXTENDED
cborObject.getExtendedFloat64()// "Regular" number, NaN, or Infinity
COMPLETE
// Check what the decoder returned:           
if (cborObject instanceof CBOR.NonFinite) {
  // Non-finite number to process
  // Select suitable method:
  cborObject.getNonFinite64()// Arbitrary non-finite number
  cborObject.getPayload()// Application-specific data
} else {
  // "Regular" number to process
  cborObject.getFloat64()// "Regular" number
}
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 | 92 | 93 |
Payload
d51-d0 in big-endian order
94 |
95 | The payload bits are conceptually put into an IEEE 754 96 | 64-bit object having the following layout: 97 |
98 |
99 | 100 | 101 |
SignExponentSignificand
011111111111d0-d51 in little-endian order
102 |
103 | For setting the sign bit, see setSign(sign). 104 |
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 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
Payload (hex)CBOR EncodingDiagnostic Notation
0f97c00Infinity
1f97e00NaN
2f97d00float'7d00'
3fff97ffffloat'7fff'
400fa7f801000float'7f801000'
7ffffffa7ffffffffloat'7fffffff'
800000fb7ff0000010000000float'7ff0000010000000'
ffffffffffffffb7ffffffffffffffffloat'7fffffffffffffff'
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 | --------------------------------------------------------------------------------