Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+13
View File
@@ -0,0 +1,13 @@
Copyright 2020 Rod Vagg
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+444
View File
@@ -0,0 +1,444 @@
# cborg - fast CBOR with a focus on strictness
[CBOR](https://cbor.io/) is "Concise Binary Object Representation", defined by [RFC 8949](https://tools.ietf.org/html/rfc8949). Like JSON, but binary, more compact, and supporting a much broader range of data types.
**cborg** focuses on strictness and deterministic data representations. CBORs flexibility leads to problems where determinism matters, such as in content-addressed data where your data encoding should converge on same-bytes for same-data. **cborg** helps aleviate these challenges.
**cborg** is also fast, and is suitable for the browser (is `Uint8Array` native) and Node.js.
**cborg** supports CBOR tags, but does not ship with them enabled by default. If you want tags, you need to plug them in to the encoder and decoder.
* [Example](#example)
* [CLI](#cli)
* [`cborg bin2diag [binary input]`](#cborg-bin2diag-binary-input)
* [`cborg bin2hex [binary string]`](#cborg-bin2hex-binary-string)
* [`cborg bin2json [--pretty] [binary input]`](#cborg-bin2json---pretty-binary-input)
* [`cborg diag2bin [diagnostic string]`](#cborg-diag2bin-diagnostic-string)
* [`cborg diag2hex [diagnostic string]`](#cborg-diag2hex-diagnostic-string)
* [`cborg diag2json [--pretty] [diagnostic string]`](#cborg-diag2json---pretty-diagnostic-string)
* [`cborg hex2bin [hex string]`](#cborg-hex2bin-hex-string)
* [`cborg hex2diag [hex string]`](#cborg-hex2diag-hex-string)
* [`cborg hex2json [--pretty] [hex string]`](#cborg-hex2json---pretty-hex-string)
* [`cborg json2bin [json string]`](#cborg-json2bin-json-string)
* [`cborg json2diag [json string]`](#cborg-json2diag-json-string)
* [`cborg json2hex '[json string]'`](#cborg-json2hex-json-string)
* [API](#api)
* [`encode(object[, options])`](#encodeobject-options)
* [Options](#options)
* [`decode(data[, options])`](#decodedata-options)
* [Options](#options-1)
* [`encodedLength(data[, options])`](#encodedlengthdata-options)
* [Type encoders](#type-encoders)
* [Tag decoders](#tag-decoders)
* [Deterministic encoding recommendations](#deterministic-encoding-recommendations)
* [Round-trip consistency](#round-trip-consistency)
* [JSON mode](#json-mode)
* [Example](#example-1)
* [License and Copyright](#license-and-copyright)
## Example
```js
import { encode, decode } from 'cborg'
const decoded = decode(Buffer.from('a16474686973a26269736543424f522163796179f5', 'hex'))
console.log('decoded:', decoded)
console.log('encoded:', encode(decoded))
```
```
decoded: { this: { is: 'CBOR!', yay: true } }
encoded: Uint8Array(21) [
161, 100, 116, 104, 105, 115,
162, 98, 105, 115, 101, 67,
66, 79, 82, 33, 99, 121,
97, 121, 245
]
```
## CLI
When installed globally via `npm` (with `npm install cborg --global`), the `cborg` command will be available that provides some handy CBOR CLI utilities. Run with `cborg help` for additional details.
The following commands take either input from the command line, or if no input is supplied will read from stdin. Output is printed to stdout. So you can `cat foo | cborg <command>`.
### `cborg bin2diag [binary input]`
Convert CBOR from binary input to a CBOR diagnostic output format which explains the byte contents.
```
$ cborg hex2bin 84616161620164f09f9880 | cborg bin2diag
84 # array(4)
61 # string(1)
61 # "a"
61 # string(1)
62 # "b"
01 # uint(1)
64 f09f # string(2)
f09f9880 # "😀"
```
### `cborg bin2hex [binary string]`
A utility method to convert a binary input (stdin only) to hexadecimal output (does not involve CBOR).
### `cborg bin2json [--pretty] [binary input]`
Convert CBOR from binary input to JSON format.
```
$ cborg hex2bin 84616161620164f09f9880 | cborg bin2json
["a","b",1,"😀"]
```
### `cborg diag2bin [diagnostic string]`
Convert a CBOR diagnostic string to a binary data form of the CBOR.
```
$ cborg json2diag '["a","b",1,"😀"]' | cborg diag2bin | cborg bin2hex
84616161620164f09f9880
```
### `cborg diag2hex [diagnostic string]`
Convert a CBOR diagnostic string to the CBOR bytes in hexadecimal format.
```
$ cborg json2diag '["a","b",1,"😀"]' | cborg diag2hex
84616161620164f09f9880
```
### `cborg diag2json [--pretty] [diagnostic string]`
Convert a CBOR diagnostic string to JSON format.
```
$ cborg json2diag '["a","b",1,"😀"]' | cborg diag2json
["a","b",1,"😀"]
```
### `cborg hex2bin [hex string]`
A utility method to convert a hex string to binary output (does not involve CBOR).
### `cborg hex2diag [hex string]`
Convert CBOR from a hexadecimal string to a CBOR diagnostic output format which explains the byte contents.
```
$ cborg hex2diag 84616161620164f09f9880
84 # array(4)
61 # string(1)
61 # "a"
61 # string(1)
62 # "b"
01 # uint(1)
64 f09f # string(2)
f09f9880 # "😀"
```
### `cborg hex2json [--pretty] [hex string]`
Convert CBOR from a hexadecimal string to JSON format.
```
$ cborg hex2json 84616161620164f09f9880
["a","b",1,"😀"]
$ cborg hex2json --pretty 84616161620164f09f9880
[
"a",
"b",
1,
"😀"
]
```
### `cborg json2bin [json string]`
Convert a JSON object into a binary data form of the CBOR.
```
$ cborg json2bin '["a","b",1,"😀"]' | cborg bin2hex
84616161620164f09f9880
```
### `cborg json2diag [json string]`
Convert a JSON object into a CBOR diagnostic output format which explains the contents of the CBOR form of the input object.
```
$ cborg json2diag '["a", "b", 1, "😀"]'
84 # array(4)
61 # string(1)
61 # "a"
61 # string(1)
62 # "b"
01 # uint(1)
64 f09f # string(2)
f09f9880 # "😀"
```
### `cborg json2hex '[json string]'`
Convert a JSON object into CBOR bytes in hexadecimal format.
```
$ cborg json2hex '["a", "b", 1, "😀"]'
84616161620164f09f9880
```
## API
### `encode(object[, options])`
```js
import { encode } from 'cborg'
```
```js
const { encode } = require('cborg')
```
Encode a JavaScript object and return a `Uint8Array` with the CBOR byte representation.
* Objects containing circular references will be rejected.
* JavaScript objects that don't have standard CBOR type representations (without tags) may be rejected or encoded in surprising ways. If you need to encode a `Date` or a `RegExp` or another exotic type, you should either form them into intermediate forms before encoding or enable a tag encoder (see [Type encoders](#type-encoders)).
* Natively supported types are: `null`, `undefined`, `number`, `bigint`, `string`, `boolean`, `Array`, `Object`, `Map`, `Buffer`, `ArrayBuffer`, `DataView`, `Uint8Array` and all other `TypedArray`s (the underlying byte array of TypedArrays is encoded, so they will all round-trip as a `Uint8Array` since the type information is lost).
* `Number`s will be encoded as integers if they don't have a fractional part (`1` and `1.0` are both considered integers, they are identical in JavaScript). Otherwise they will be encoded as floats.
* Integers will be encoded to their smallest possible representations: compacted (into the type byte), 8-bit, 16-bit, 32-bit or 64-bit.
* Integers larger than `Number.MAX_SAFE_INTEGER` or less than `Number.MIN_SAFE_INTEGER` will be encoded as floats. There is no way to safely determine whether a number has a fractional part outside of this range.
* `BigInt`s are supported by default within the 64-bit unsigned range but will be also be encoded to their smallest possible representation (so will not round-trip as a `BigInt` if they are smaller than `Number.MAX_SAFE_INTEGER`). Larger `BigInt`s require a tag (officially tags 2 and 3).
* Floats will be encoded in their smallest possible representations: 16-bit, 32-bit or 64-bit. Unless the `float64` option is supplied.
* Object properties are sorted according to the original [RFC 7049](https://tools.ietf.org/html/rfc7049) canonical representation recommended method: length-first and then bytewise. Note that this recommendation has changed in [RFC 8949](https://tools.ietf.org/html/rfc8949) to be plain bytewise (this is not currently supported but pull requests are welcome to add it as an option).
* The only CBOR major 7 "simple values" supported are `true`, `false`, `undefined` and `null`. "Simple values" outside of this range are intentionally not supported (pull requests welcome to enable them with an option).
* Objects, arrays, strings and bytes are encoded as fixed-length, encoding as indefinite length is intentionally not supported.
#### Options
* `float64` (boolean, default `false`): do not attempt to store floats as their smallest possible form, store all floats as 64-bit
* `typeEncoders` (object): a mapping of type name to function that can encode that type into cborg tokens. This may also be used to reject or transform types as objects are dissected for encoding. See the [Type encoders](#type-encoders) section below for more information.
* `mapSorter` (function): a function taking two arguments, where each argument is a `Token`, or an array of `Token`s representing the keys of a map being encoded. Similar to other JavaScript compare functions, a `-1`, `1` or `0` (which shouldn't be possible) should be returned depending on the sorting order of the keys. See the source code for the default sorting order which uses the length-first rule recommendation from [RFC 7049](https://tools.ietf.org/html/rfc7049).
### `decode(data[, options])`
```js
import { decode } from 'cborg'
```
```js
const { decode } = require('cborg')
```
Decode valid CBOR bytes from a `Uint8Array` (or `Buffer`) and return a JavaScript object.
* Integers (major 0 and 1) that are outside of the safe integer range will be converted to a `BigInt`.
* The only CBOR major 7 "simple values" supported are `true`, `false`, `undefined` and `null`. "Simple values" outside of this range are intentionally not supported (pull requests welcome to enable them with an option).
* Indefinite length strings and byte arrays are intentionally not supported (pull requests welcome to enable them with an option). Although indefinite length arrays and maps are supported by default.
#### Options
* `allowIndefinite` (boolean, default `true`): when the indefinite length additional information (`31`) is encountered for any type (arrays, maps, strings, bytes) _or_ a "break" is encountered, an error will be thrown.
* `allowUndefined` (boolean, default `true`): when major 7, minor 23 (`undefined`) is encountered, an error will be thrown. To disallow `undefined` on encode, a custom [type encoder](#type-encoders) for `'undefined'` will need to be supplied.
* `coerceUndefinedToNull` (boolean, default `false`): when both `allowUndefined` and `coerceUndefinedToNull` are set to `true`, all `undefined` tokens (major `7` minor `23`: `0xf7`) will be coerced to `null` tokens, such that `undefined` is an allowed token but will not appear in decoded values.
* `allowInfinity` (boolean, default `true`): when an IEEE 754 `Infinity` or `-Infinity` value is encountered when decoding a major 7, an error will be thrown. To disallow `Infinity` and `-Infinity` on encode, a custom [type encoder](#type-encoders) for `'number'` will need to be supplied.
* `allowNaN` (boolean, default `true`): when an IEEE 754 `NaN` value is encountered when decoding a major 7, an error will be thrown. To disallow `NaN` on encode, a custom [type encoder](#type-encoders) for `'number'` will need to be supplied.
* `allowBigInt` (boolean, default `true`): when an integer outside of the safe integer range is encountered, an error will be thrown. To disallow `BigInt`s on encode, a custom [type encoder](#type-encoders) for `'bigint'` will need to be supplied.
* `strict` (boolean, default `false`): when decoding integers, including for lengths (arrays, maps, strings, bytes), values will be checked to see whether they were encoded in their smallest possible form. If not, an error will be thrown.
* Currently, this form of deterministic strictness cannot be enforced for float representations, or map key ordering (pull requests _very_ welcome).
* `useMaps` (boolean, default `false`): when decoding major 5 (map) entries, use a `Map` rather than a plain `Object`. This will nest for any encountered map. During encode, a `Map` will be interpreted as an `Object` and will round-trip as such unless `useMaps` is supplied, in which case, all `Map`s and `Object`s will round-trip as `Map`s. There is no way to retain the distinction during round-trip without using a custom tag.
* `rejectDuplicateMapKeys` (boolean, default `false`): when the decoder encounters duplicate keys for the same map, an error will be thrown when this option is set. This is an additional _strictness_ option, disallowing data-hiding and reducing the number of same-data different-bytes possibilities where it matters.
* `retainStringBytes` (boolean, default `false`): when decoding strings, retain the original bytes on the `Token` object as `byteValue`. Since it is possible to encode non-UTF-8 characters in strings in CBOR, and JavaScript doesn't properly handle non-UTF-8 in its conversion from bytes (`TextEncoder` or `Buffer`), this can result in a loss of data (and an inability to round-trip). Where this is important, a token stream should be consumed instead of a plain `decode()` and the `byteValue` property on string tokens can be inspected (see [lib/diagnostic.js](lib/diagnostic.js) for an example of its use.)
* `tags` (array): a mapping of tag number to tag decoder function. By default no tags are supported. See [Tag decoders](#tag-decoders).
* `tokenizer` (object): an object with two methods, `next()` which returns a `Token` and `done()` which returns a `boolean`. Can be used to implement custom input decoding. See the source code for examples.
### `encodedLength(data[, options])`
```js
import { encodedLength } from 'cborg/length'
```
```js
const { encodedLength } = require('cborg/length')
```
Calculate the byte length of the given data when encoded as CBOR with the options provided. The options are the same as for an `encode()` call. This calculation will be accurate if the same options are used as when performing a normal `encode()`. Some encode options can change the encoding output length.
A `tokensToLength()` function is available which deals directly with a tokenized form of the object, but this only recommended for advanced users.
### Type encoders
The `typeEncoders` property to the `options` argument to `encode()` allows you to add additional functionality to cborg, or override existing functionality.
When converting JavaScript objects, types are differentiated using the method and naming used by [@sindresorhus/is](https://github.com/sindresorhus/is) _(a custom implementation is used internally for performance reasons)_ and an internal set of type encoders are used to convert objects to their appropriate CBOR form. Supported types are: `null`, `undefined`, `number`, `bigint`, `string`, `boolean`, `Array`, `Object`, `Map`, `Buffer`, `ArrayBuffer`, `DataView`, `Uint8Array` and all other `TypedArray`s (their underlying byte array is encoded, so they will all round-trip as a `Uint8Array` since the type information is lost). Any object that doesn't match a type in this list will cause an error to be thrown during decode. e.g. `encode(new Date())` will throw an error because there is no internal `Date` type encoder.
The `typeEncoders` option is an object whose property names match to @sindresorhus/is type names. When this option is provided and a property exists for any given object's type, the function provided as the value to that property is called with the object as an argument.
If a type encoder function returns `null`, the default encoder, if any, is used instead.
If a type encoder function returns an array, cborg will expect it to contain zero or more `Token` objects that will be encoded to binary form.
`Token`s map directly to CBOR entities. Each one has a `Type` and a `value`. A type encoder is responsible for turning a JavaScript object into a set of tags.
This example is available from the cborg taglib as `bigIntEncoder` (`import { bigIntEncoder } as taglib from 'cborg/taglib'`) and implements CBOR tags 2 and 3 (bigint and negative bigint). This function would be registered using an options parameter `{ typeEncoders: { bigint: bigIntEncoder } }`. All objects that have a type `bigint` will pass through this function.
```js
import { Token, Type } from './cborg.js'
function bigIntEncoder (obj) {
// check whether this BigInt could fit within a standard CBOR 64-bit int or less
if (obj >= -1n * (2n ** 64n) && obj <= (2n ** 64n) - 1n) {
return null // handle this as a standard int or negint
}
// it's larger than a 64-bit int, encode as tag 2 (positive) or 3 (negative)
return [
new Token(Type.tag, obj >= 0n ? 2 : 3),
new Token(Type.bytes, fromBigInt(obj >= 0n ? obj : obj * -1n - 1n))
]
}
function fromBigInt (i) { /* returns a Uint8Array, omitted from example */ }
```
This example encoder demonstrates the ability to pass-through to the default encoder, or convert to a series of custom tags. In this case we can put any arbitrarily large `BigInt` into a byte array using the standard CBOR tag 2 and 3 types.
Valid `Token` types for the second argument to `Token()` are:
```js
Type.uint
Type.negint
Type.bytes
Type.string
Type.array
Type.map
Type.tag
Type.float
Type.false
Type.true
Type.null
Type.undefined
Type.break
```
Using type encoders we can:
* Override the default encoder entirely (always return an array of `Token`s)
* Override the default encoder for a subset of values (use `null` as a pass-through)
* Omit an object type entirely from the encode (return an empty array)
* Convert an object to something else entirely (such as a tag, or make all `number`s into floats)
* Throw if something should that is supported should be unsupported (e.g. `undefined`)
### Tag decoders
By default cborg does not support decoding of any tags. Where a tag is encountered during decode, an error will be thrown. If tag support is needed, they will need to be supplied as options to the `decode()` function. The `tags` property should contain an array where the indexes correspond to the tag numbers that are encountered during decode, and the values are functions that are able to turn the following token(s) into a JavaScript object. Each tag token in CBOR is followed by a data item, often a byte array of arbitrary length, but can be a more complex series of tokens that form a nested data item. This token is supplied to the tag decoder function.
This example is available from the cborg taglib as `bigIntDecoder` and `bigNegIntDecoder` (`import { bigIntDecoder, bigNegIntDecoder } as taglib from 'cborg/taglib'`) and implements CBOR tags 2 and 3 (bigint and negative bigint). This function would be registered using an options parameter:
```js
const tags = []
tags[2] = bigIntDecoder
tags[3] = bigNegIntDecoder
decode(bytes, { tags })
```
Implementation:
```js
function bigIntDecoder (bytes) {
let bi = 0n
for (let ii = 0; ii < bytes.length; ii++) {
bi = (bi << 8n) + BigInt(bytes[ii])
}
return bi
}
function bigNegIntDecoder (bytes) {
return -1n - bigIntDecoder(bytes)
}
```
## Deterministic encoding recommendations
cborg is designed with deterministic encoding forms as a primary feature. It is suitable for use with content addressed systems or other systems where convergence of binary forms is important. The ideal is to have strictly _one way_ of mapping a set of data into a binary form. Unfortunately CBOR has many opportunities for flexibility, including:
* Varying number sizes and no strict requirement for their encoding - e.g. a `1` may be encoded as `0x01`, `0x1801`, `0x190001`, `1a00000001` or `1b0000000000000001`.
* Varying int sizes used as lengths for lengthed objects (maps, arrays, strings, bytes) - e.g. a single entry array could specify its length using any of the above forms for `1`. Tags can also vary in size and still represent the same number.
* IEEE 754 allows for `NaN`, `Infinity` and `-Infinity` to be represented in many different ways, meaning it is possible to represent the same data using many different byte forms.
* Indefinite length items where the length is omitted from the additional item of the entity token and a "break" is inserted to indicate the end of of the object. This provides two ways to encode the same object.
* Tags that can allow alternative representations of objects - e.g. using the bigint or negative bigint tags to represent standard size integers.
* Map ordering is flexible by default, so a single map can be represented in many different forms by shuffling the keys.
* Many CBOR decoders ignore trailing bytes that are not part of an initial object. This can be helpful to support streaming-CBOR, but opens avenues for byte padding.
By default, cborg will always **encode** objects to the same bytes by applying some strictness rules:
* Using smallest-possible representations for ints, negative ints, floats and lengthed object lengths.
* Always sorting maps using the _original_ recommended [RFC 7049](https://tools.ietf.org/html/rfc7049) map key ordering rules.
* Omitting support for tags (therefore omitting support for exotic object types).
* Applying deterministic rules to `number` differentiation - if a fractional part is missing and it's within the safe integer boundary, it's encoded as an integer, otherwise it's encoded as a float.
By default, cborg allows for some flexibility on **decode** of objects, which will present some challenges if users wish to impose strictness requirements at both serialization _and_ deserialization. Options that can be provided to `decode()` to impose some strictness requirements are:
* `strict: true` to impose strict sizing rules for int, negative ints and lengths of lengthed objects
* `allowNaN: false` and `allowInfinity` to prevent decoding of any value that would resolve to `NaN`, `Infinity` or `-Infinity`, using CBOR tokens or IEEE 754 representation—as long as your application can do without these symbols.
* `allowIndefinite: false` to disallow indefinite lengthed objects and the "break" tag
* Not providing any tag decoders, or ensuring that tag decoders are strict about their forms (e.g. a bigint decoder could reject bigints that could have fit into a standard major 0 64-bit integer).
* Overriding type decoders where they may introduce undesired flexibility.
Currently, there are two areas that cborg cannot impose strictness requirements (pull requests welcome!):
* Smallest-possible floats, or always-float64 cannot be enforced on decode.
* Map ordering cannot be enforced on decode.
### Round-trip consistency
There are a number of forms where an object will not round-trip precisely, if this matters for an application, care should be taken, or certain types should be disallowed entirely during encode.
* All `TypedArray`s will decode as `Uint8Array`s, unless a custom tag is used.
* Both `Map` and `Object` will be encoded as a CBOR `map`, as will any other object that inherits from `Object` that can't be differentiated by the [@sindresorhus/is](https://github.com/sindresorhus/is) algorithm. They will all decode as `Object` by default, or `Map` if `useMaps` is set to `true`. e.g. `{ foo: new Map() }` will round-trip to `{ foo: {} }` by default.
## JSON mode
**cborg** can also encode and decode JSON using the same pipeline and many of the same settings. For most (but not all) cases it will be faster to use `JSON.parse()` and `JSON.stringify()`, however **cborg** provides much more control over the process to handle determinism and be more restrictive in allowable forms. It also operates natively with Uint8Arrays rather than strings which may also offer some minor efficiency or usability gains in some circumstances.
Use `import { encode, decode } from 'cborg/json'` or `const { encode, decode } = require('cborg/json')` to access the JSON handling encoder and decoder.
Many of the same encode and decode options available for CBOR can be used to manage JSON handling. These include strictness requirements for decode and custom tag encoders for encode. Tag encoders can't create new tags as there are no tags in JSON, but they can replace JavaScript object forms with custom JSON forms (e.g. convert a `Uint8Array` to a valid JSON form rather than having the encoder throw an error). The inverse is also possible, turning specific JSON forms into JavaScript forms, by using a custom tokenizer on decode.
Special notes on options specific to the JSON:
* Decoder `allowBigInt` option: is repurposed for the JSON decoder and defaults to `false`. When `false`, all numbers are decoded as `Number`, possibly losing precision when encountering numbers outside of the JavaScript safe integer range. When `true` numbers that have a decimal point (`.`, even if just `.0`) are returned as a `Number`, but for numbers without a decimal point _and_ that are outside of the JavaScript safe integer range, they are returned as `BigInt`s. This behaviour differs from CBOR decoding which will error when decoding integer and negative integer tokens that are outside of the JavaScript safe integer range if `allowBigInt` is `false`.
See **[@ipld/dag-json](https://github.com/ipld/js-dag-json)** for an advanced use of the **cborg** JSON encoder and decoder including round-tripping of `Uint8Array`s and custom JavaScript classes (IPLD `CID` objects in this case).
### Example
Similar to the [CBOR example above](#example), using JSON:
```js
import { encode, decode } from 'cborg/json'
const decoded = decode(Buffer.from('7b2274686973223a7b226973223a224a534f4e21222c22796179223a747275657d7d', 'hex'))
console.log('decoded:', decoded)
console.log('encoded:', encode(decoded))
console.log('encoded (string):', Buffer.from(encode(decoded)).toString())
```
```
decoded: { this: { is: 'JSON!', yay: true } }
encoded: Uint8Array(34) [
123, 34, 116, 104, 105, 115, 34, 58,
123, 34, 105, 115, 34, 58, 34, 74,
83, 79, 78, 33, 34, 44, 34, 121,
97, 121, 34, 58, 116, 114, 117, 101,
125, 125
]
encoded (string): {"this":{"is":"JSON!","yay":true}}
```
## License and Copyright
Copyright 2020 Rod Vagg
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+19
View File
@@ -0,0 +1,19 @@
import { encode } from './lib/encode.js'
import { decode } from './lib/decode.js'
import { Token, Type } from './lib/token.js'
/**
* Export the types that were present in the original manual cborg.d.ts
* @typedef {import('./interface').TagDecoder} TagDecoder
* There was originally just `TypeEncoder` so don't break types by renaming or not exporting
* @typedef {import('./interface').OptionalTypeEncoder} TypeEncoder
* @typedef {import('./interface').DecodeOptions} DecodeOptions
* @typedef {import('./interface').EncodeOptions} EncodeOptions
*/
export {
decode,
encode,
Token,
Type
}
+643
View File
@@ -0,0 +1,643 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const fixtures = [
{
cbor: 'AA==',
hex: '00',
roundtrip: true,
decoded: 0
},
{
cbor: 'AQ==',
hex: '01',
roundtrip: true,
decoded: 1
},
{
cbor: 'Cg==',
hex: '0a',
roundtrip: true,
decoded: 10
},
{
cbor: 'Fw==',
hex: '17',
roundtrip: true,
decoded: 23
},
{
cbor: 'GBg=',
hex: '1818',
roundtrip: true,
decoded: 24
},
{
cbor: 'GBk=',
hex: '1819',
roundtrip: true,
decoded: 25
},
{
cbor: 'GGQ=',
hex: '1864',
roundtrip: true,
decoded: 100
},
{
cbor: 'GQPo',
hex: '1903e8',
roundtrip: true,
decoded: 1000
},
{
cbor: 'GgAPQkA=',
hex: '1a000f4240',
roundtrip: true,
decoded: 1000000
},
{
cbor: 'GwAAAOjUpRAA',
hex: '1b000000e8d4a51000',
roundtrip: true,
decoded: 1000000000000
},
{
cbor: 'G///////////',
hex: '1bffffffffffffffff',
roundtrip: true,
decoded: BigInt('18446744073709551615')
},
{
cbor: 'wkkBAAAAAAAAAAA=',
hex: 'c249010000000000000000',
roundtrip: true,
decoded: BigInt('18446744073709551616'),
noTagDecodeError: /tag not supported \(2\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'O///////////',
hex: '3bffffffffffffffff',
roundtrip: true,
decoded: BigInt('-18446744073709551616')
},
{
cbor: 'w0kBAAAAAAAAAAA=',
hex: 'c349010000000000000000',
roundtrip: true,
decoded: BigInt('-18446744073709551617'),
noTagDecodeError: /tag not supported \(3\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'IA==',
hex: '20',
roundtrip: true,
decoded: -1
},
{
cbor: 'KQ==',
hex: '29',
roundtrip: true,
decoded: -10
},
{
cbor: 'OGM=',
hex: '3863',
roundtrip: true,
decoded: -100
},
{
cbor: 'OQPn',
hex: '3903e7',
roundtrip: true,
decoded: -1000
},
{
cbor: '+QAA',
hex: 'f90000',
roundtrip: false,
decoded: 0
},
{
cbor: '+YAA',
hex: 'f98000',
roundtrip: false,
decoded: -0
},
{
cbor: '+TwA',
hex: 'f93c00',
roundtrip: false,
decoded: 1
},
{
cbor: '+z/xmZmZmZma',
hex: 'fb3ff199999999999a',
roundtrip: true,
decoded: 1.1
},
{
cbor: '+T4A',
hex: 'f93e00',
roundtrip: true,
decoded: 1.5
},
{
cbor: '+Xv/',
hex: 'f97bff',
roundtrip: false,
decoded: 65504
},
{
cbor: '+kfDUAA=',
hex: 'fa47c35000',
roundtrip: false,
decoded: 100000
},
{
cbor: '+n9///8=',
hex: 'fa7f7fffff',
roundtrip: true,
decoded: 3.4028234663852886e+38
},
{
cbor: '+3435DyIAHWc',
hex: 'fb7e37e43c8800759c',
roundtrip: true,
decoded: 1e+300
},
{
cbor: '+QAB',
hex: 'f90001',
roundtrip: true,
decoded: 5.960464477539063e-8
},
{
cbor: '+QQA',
hex: 'f90400',
roundtrip: true,
decoded: 0.00006103515625
},
{
cbor: '+cQA',
hex: 'f9c400',
roundtrip: false,
decoded: -4
},
{
cbor: '+8AQZmZmZmZm',
hex: 'fbc010666666666666',
roundtrip: true,
decoded: -4.1
},
{
cbor: '+XwA',
hex: 'f97c00',
roundtrip: true,
diagnostic: Infinity
},
{
cbor: '+X4A',
hex: 'f97e00',
roundtrip: true,
diagnostic: NaN
},
{
cbor: '+fwA',
hex: 'f9fc00',
roundtrip: true,
diagnostic: -Infinity
},
{
cbor: '+n+AAAA=',
hex: 'fa7f800000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+n/AAAA=',
hex: 'fa7fc00000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+v+AAAA=',
hex: 'faff800000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '+3/wAAAAAAAA',
hex: 'fb7ff0000000000000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+3/4AAAAAAAA',
hex: 'fb7ff8000000000000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+//wAAAAAAAA',
hex: 'fbfff0000000000000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '9A==',
hex: 'f4',
roundtrip: true,
decoded: false
},
{
cbor: '9Q==',
hex: 'f5',
roundtrip: true,
decoded: true
},
{
cbor: '9g==',
hex: 'f6',
roundtrip: true,
decoded: null
},
{
cbor: '9w==',
hex: 'f7',
roundtrip: true,
diagnostic: undefined
},
{
cbor: '8A==',
hex: 'f0',
roundtrip: true,
diagnostic: 'simple(16)',
error: /simple values are not supported/
},
{
cbor: '+Bg=',
hex: 'f818',
roundtrip: true,
diagnostic: 'simple(24)',
error: /simple values are not supported/
},
{
cbor: '+P8=',
hex: 'f8ff',
roundtrip: true,
diagnostic: 'simple(255)',
error: /simple values are not supported/
},
{
cbor: 'wHQyMDEzLTAzLTIxVDIwOjA0OjAwWg==',
hex: 'c074323031332d30332d32315432303a30343a30305a',
roundtrip: false,
diagnostic: '0("2013-03-21T20:04:00Z")'
},
{
cbor: 'wRpRS2ew',
hex: 'c11a514b67b0',
roundtrip: false,
diagnostic: '1(1363896240)'
},
{
cbor: 'wftB1FLZ7CAAAA==',
hex: 'c1fb41d452d9ec200000',
roundtrip: false,
diagnostic: '1(1363896240.5)'
},
{
cbor: '10QBAgME',
hex: 'd74401020304',
roundtrip: false,
diagnostic: '23(h\'01020304\')'
},
{
cbor: '2BhFZElFVEY=',
hex: 'd818456449455446',
roundtrip: false,
diagnostic: '24(h\'6449455446\')'
},
{
cbor: '2CB2aHR0cDovL3d3dy5leGFtcGxlLmNvbQ==',
hex: 'd82076687474703a2f2f7777772e6578616d706c652e636f6d',
roundtrip: false,
diagnostic: '32("http://www.example.com")'
},
{
cbor: 'QA==',
hex: '40',
roundtrip: true,
diagnostic: 'h\'\''
},
{
cbor: 'RAECAwQ=',
hex: '4401020304',
roundtrip: true,
diagnostic: 'h\'01020304\''
},
{
cbor: 'YA==',
hex: '60',
roundtrip: true,
decoded: ''
},
{
cbor: 'YWE=',
hex: '6161',
roundtrip: true,
decoded: 'a'
},
{
cbor: 'ZElFVEY=',
hex: '6449455446',
roundtrip: true,
decoded: 'IETF'
},
{
cbor: 'YiJc',
hex: '62225c',
roundtrip: true,
decoded: '"\\'
},
{
cbor: 'YsO8',
hex: '62c3bc',
roundtrip: true,
decoded: 'ü'
},
{
cbor: 'Y+awtA==',
hex: '63e6b0b4',
roundtrip: true,
decoded: '水'
},
{
cbor: 'ZPCQhZE=',
hex: '64f0908591',
roundtrip: true,
decoded: '\uD800\uDD51'
},
{
cbor: 'gA==',
hex: '80',
roundtrip: true,
decoded: []
},
{
cbor: 'gwECAw==',
hex: '83010203',
roundtrip: true,
decoded: [
1,
2,
3
]
},
{
cbor: 'gwGCAgOCBAU=',
hex: '8301820203820405',
roundtrip: true,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'mBkBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgYGBk=',
hex: '98190102030405060708090a0b0c0d0e0f101112131415161718181819',
roundtrip: true,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'oA==',
hex: 'a0',
roundtrip: true,
decoded: {}
},
{
cbor: 'ogECAwQ=',
hex: 'a201020304',
roundtrip: true,
diagnostic: '{1: 2, 3: 4}',
error: /non-string keys not supported/
},
{
cbor: 'omFhAWFiggID',
hex: 'a26161016162820203',
roundtrip: true,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhoWFiYWM=',
hex: '826161a161626163',
roundtrip: true,
decoded: [
'a',
{ b: 'c' }
]
},
{
cbor: 'pWFhYUFhYmFCYWNhQ2FkYURhZWFF',
hex: 'a56161614161626142616361436164614461656145',
roundtrip: true,
decoded: {
a: 'A',
b: 'B',
c: 'C',
d: 'D',
e: 'E'
}
},
{
cbor: 'X0IBAkMDBAX/',
hex: '5f42010243030405ff',
roundtrip: false,
diagnostic: '(_ h\'0102\', h\'030405\')',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'f2VzdHJlYWRtaW5n/w==',
hex: '7f657374726561646d696e67ff',
roundtrip: false,
decoded: 'streaming',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'n/8=',
hex: '9fff',
roundtrip: false,
decoded: []
},
{
cbor: 'nwGCAgOfBAX//w==',
hex: '9f018202039f0405ffff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwGCAgOCBAX/',
hex: '9f01820203820405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGCAgOfBAX/',
hex: '83018202039f0405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGfAgP/ggQF',
hex: '83019f0203ff820405',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwECAwQFBgcICQoLDA0ODxAREhMUFRYXGBgYGf8=',
hex: '9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff',
roundtrip: false,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'v2FhAWFinwID//8=',
hex: 'bf61610161629f0203ffff',
roundtrip: false,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhv2FiYWP/',
hex: '826161bf61626163ff',
roundtrip: false,
decoded: [
'a',
{ b: 'c' }
]
},
{
cbor: 'v2NGdW71Y0FtdCH/',
hex: 'bf6346756ef563416d7421ff',
roundtrip: false,
decoded: {
Fun: true,
Amt: -2
}
}
];
exports.fixtures = fixtures;
+24
View File
@@ -0,0 +1,24 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('../lib/token.js');
function dateDecoder(obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1');
}
return new Date(obj);
}
function dateEncoder(obj) {
if (!(obj instanceof Date)) {
throw new Error('expected Date for "Date" encoder');
}
return [
new token.Token(token.Type.tag, 0),
new token.Token(token.Type.string, obj.toISOString().replace(/\.000Z$/, 'Z'))
];
}
exports.dateDecoder = dateDecoder;
exports.dateEncoder = dateEncoder;
+348
View File
@@ -0,0 +1,348 @@
'use strict';
var chai = require('chai');
var child_process = require('child_process');
var process = require('process');
var path = require('path');
var os = require('os');
var url = require('url');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
var process__default = /*#__PURE__*/_interopDefaultLegacy(process);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
const {assert} = chai__default["default"];
const fixture1JsonString = '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}';
const fixture1JsonPrettyString = `{
"a": 1,
"b": [
2,
3
],
"smile": "😀"
}
`;
const fixture1HexString = 'a3616101616282020365736d696c6564f09f9880';
const fixture1Bin = fromHex(fixture1HexString);
const fixture1BinString = new TextDecoder().decode(fixture1Bin);
const fixture1DiagnosticString = `a3 # map(3)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`;
const fixture2HexString = 'a4616101616282020363627566440102036165736d696c6564f09f9880';
const fixture2DiagnosticString = `a4 # map(4)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
63 # string(3)
627566 # "buf"
44 # bytes(4)
01020361 # "\\x01\\x02\\x03a"
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`;
const binPath = path__default["default"].join(path__default["default"].dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('browser-test/node-test-bin.js', document.baseURI).href)))), '../lib/bin.js');
function fromHex(hex) {
return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
}
async function execBin(cmd, stdin) {
return new Promise((resolve, reject) => {
const cp = child_process.exec(`"${ process__default["default"].execPath }" "${ binPath }" ${ cmd }`, (err, stdout, stderr) => {
if (err) {
err.stdout = stdout;
err.stderr = stderr;
return reject(err);
}
resolve({
stdout,
stderr
});
});
if (stdin != null) {
cp.on('spawn', () => {
cp.stdin.write(stdin);
cp.stdin.end();
});
}
});
}
describe('Bin', () => {
it('usage', async () => {
try {
await execBin('');
assert.fail('should have errored');
} catch (e) {
assert.strictEqual(e.stdout, '');
assert.strictEqual(e.stderr, `Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
}
});
it('bad cmd', async () => {
try {
await execBin('blip');
assert.fail('should have errored');
} catch (e) {
assert.strictEqual(e.stdout, '');
assert.strictEqual(e.stderr, `Unknown command: 'blip'
Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
}
});
it('help', async () => {
const {stdout, stderr} = await execBin('help');
assert.strictEqual(stdout, '');
assert.strictEqual(stderr, `Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
});
it('bin2diag (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2diag', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1DiagnosticString);
});
it('bin2hex (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2hex', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
it('bin2json (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2json', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it('bin2json pretty (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2json --pretty', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
for (const stdin of [
true,
false
]) {
if (os.platform() !== 'win32' || stdin) {
it(`diag2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2bin '${ fixture1DiagnosticString }'`) : await execBin('diag2bin', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`diag2hex${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2hex '${ fixture1DiagnosticString }'`) : await execBin('diag2hex', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
it(`diag2json${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2json '${ fixture1DiagnosticString }'`) : await execBin('diag2json', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it(`diag2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2json --pretty '${ fixture1DiagnosticString }'`) : await execBin('diag2json --pretty', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
}
it(`hex2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2bin ${ fixture1HexString }`) : await execBin('hex2bin', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`hex2diag${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2diag ${ fixture2HexString }`) : await execBin('hex2diag', fixture2HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture2DiagnosticString);
});
it(`hex2json${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2json ${ fixture1HexString }`) : await execBin('hex2json', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it(`hex2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2json --pretty ${ fixture1HexString }`) : await execBin('hex2json --pretty', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
it(`json2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2bin', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`json2diag${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2diag', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1DiagnosticString);
});
it(`json2hex${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`json2hex "${ fixture1JsonString.replace(/"/g, '\\"') }"`) : await execBin('json2hex', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
}
it('diag indenting', async () => {
const {stdout, stderr} = await execBin('json2diag', '{"a":[],"b":{},"c":{"a":1,"b":{"a":{"a":{}}}},"d":{"a":{"a":{"a":1},"b":2,"c":[]}},"e":[[[[{"a":{}}]]]],"f":1}');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `a6 # map(6)
61 # string(1)
61 # "a"
80 # array(0)
61 # string(1)
62 # "b"
a0 # map(0)
61 # string(1)
63 # "c"
a2 # map(2)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
a1 # map(1)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
64 # "d"
a1 # map(1)
61 # string(1)
61 # "a"
a3 # map(3)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
02 # uint(2)
61 # string(1)
63 # "c"
80 # array(0)
61 # string(1)
65 # "e"
81 # array(1)
81 # array(1)
81 # array(1)
81 # array(1)
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
66 # "f"
01 # uint(1)
`);
});
describe('diag length bytes', () => {
it('compact', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `77 # string(23)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
`);
});
it('1-byte', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `78 23 # string(35)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161616161616161616161 # "aaaaaaaaaaaa"
`);
});
it('2-byte', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `79 0100 # string(256)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161 # "aaa"
`);
});
});
it('diag non-utf8 and non-printable ascii', async () => {
const input = '7864f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c11e756338bd93865e645f1adec9b9c99ef407fbd4fc6859e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82f9f18c3d03418e35';
let {stdout, stderr} = await execBin(`hex2diag ${ input }`);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `78 64 # string(86)
f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b # "õ_øñ%\\x08¶>ò¿ì§Uzé\\x0dö1\\x1a^Ác\\x1b"
4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c # "J\\x1f¨C1\\x0bÙç\\x10ê¬å¡½×*пàIw\\x1c"
11e756338bd93865e645f1adec9b9c99ef407fbd4fc685 # "\\x11çV3\\x8bÙ8eæEñ\\xadì\\x9b\\x9c\\x99ï@\\x7f½OÆ\\x85"
9e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82 # "\\x9ey\\x04Å\\xad}ɽ\\x10¥Ì\\x16\\x97=[(ì\\x1amÔ=\\x9f\\x82"
f9f18c3d03418e35 # "ùñ\\x8c=\\x03A\\x8e5"
`);
({stdout, stderr} = await execBin('diag2hex', stdout));
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ input }\n`);
});
});
+4
View File
@@ -0,0 +1,4 @@
'use strict';
require('../lib/bin.js');
+158
View File
@@ -0,0 +1,158 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '00',
expected: 0,
type: 'uint8'
},
{
data: '02',
expected: 2,
type: 'uint8'
},
{
data: '18ff',
expected: 255,
type: 'uint8'
},
{
data: '1901f4',
expected: 500,
type: 'uint16'
},
{
data: '1900ff',
expected: 255,
type: 'uint16',
strict: false
},
{
data: '19ffff',
expected: 65535,
type: 'uint16'
},
{
data: '1a000000ff',
expected: 255,
type: 'uint32',
strict: false
},
{
data: '1a00010000',
expected: 65536,
type: 'uint32'
},
{
data: '1a000f4240',
expected: 1000000,
type: 'uint32'
},
{
data: '1aa5f702b3',
expected: 2784428723,
type: 'uint32'
},
{
data: '1b00000000000000ff',
expected: 255,
type: 'uint64',
strict: false
},
{
data: '1b0016db6db6db6db7',
expected: Number.MAX_SAFE_INTEGER / 1.4,
type: 'uint64'
},
{
data: '1b001fffffffffffff',
expected: Number.MAX_SAFE_INTEGER,
type: 'uint64'
},
{
data: '1ba5f702b3a5f702b3',
expected: BigInt('11959030306112471731'),
type: 'uint64'
},
{
data: '1bffffffffffffffff',
expected: BigInt('18446744073709551615'),
type: 'uint64'
}
];
describe('uint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(data) === fixture.expected, `decode ${ fixture.type } ${ decode.decode(data) } != ${ fixture.expected }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.ok(decode.decode(data, { strict: true }) === fixture.expected, `decode ${ fixture.type }`);
}
});
}
});
it('should throw error', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('1ca5f702b3a5f702b3')), Error, 'CBOR decode error: encountered invalid minor (28) for major 0');
assert.throws(() => decode.decode(byteUtils.fromHex('1ba5f702b3a5f702')), Error, 'CBOR decode error: not enough data for type');
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(encode.encode(fixture.expected)) === fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('toobig', () => {
it('bigger than 64-bit', () => {
assert.doesNotThrow(() => encode.encode(BigInt('18446744073709551615')));
assert.throws(() => encode.encode(BigInt('18446744073709551616')), /BigInt larger than allowable range/);
});
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode.decode(data, { allowBigInt: false }), /safe integer range/);
} else {
assert.ok(decode.decode(data, { allowBigInt: false }) === fixture.expected, `decode ${ fixture.type }`);
}
}
});
});
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected);
it(`should encode ${ small }n`, () => {
assert.strictEqual(byteUtils.toHex(encode.encode(BigInt(small))), fixture.data, `encode ${ small }`);
});
}
}
});
});
+152
View File
@@ -0,0 +1,152 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '20',
expected: -1,
type: 'negint8'
},
{
data: '22',
expected: -3,
type: 'negint8'
},
{
data: '3863',
expected: -100,
type: 'negint8'
},
{
data: '38ff',
expected: -256,
type: 'negint8'
},
{
data: '3900ff',
expected: -256,
type: 'negint16',
strict: false
},
{
data: '3901f4',
expected: -501,
type: 'negint16'
},
{
data: '3a000000ff',
expected: -256,
type: 'negint32',
strict: false
},
{
data: '3aa5f702b3',
expected: -2784428724,
type: 'negint32'
},
{
data: '3b00000000000000ff',
expected: -256,
type: 'negint32',
strict: false
},
{
data: '3b0016db6db6db6db7',
expected: Number.MIN_SAFE_INTEGER / 1.4 - 1,
type: 'negint64'
},
{
data: '3b001ffffffffffffe',
expected: Number.MIN_SAFE_INTEGER,
type: 'negint64'
},
{
data: '3b001fffffffffffff',
expected: BigInt('-9007199254740992'),
type: 'negint64'
},
{
data: '3b0020000000000000',
expected: BigInt('-9007199254740993'),
type: 'negint64'
},
{
data: '3ba5f702b3a5f702b3',
expected: BigInt('-11959030306112471732'),
type: 'negint64'
},
{
data: '3bffffffffffffffff',
expected: BigInt('-18446744073709551616'),
type: 'negint64'
}
];
describe('negint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(data) === fixture.expected, `decode ${ fixture.type } (${ decode.decode(data) } != ${ fixture.expected })`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.strictEqual(decode.decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
}
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(encode.encode(fixture.expected)) === fixture.expected, `roundtrip ${ fixture.type }`);
});
}
});
describe('toobig', () => {
it('bigger than 64-bit', () => {
assert.doesNotThrow(() => encode.encode(BigInt('-18446744073709551616')));
assert.throws(() => encode.encode(BigInt('-18446744073709551617')), /BigInt larger than allowable range/);
});
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode.decode(data, { allowBigInt: false }), /safe integer range/);
} else {
assert.ok(decode.decode(data, { allowBigInt: false }) === fixture.expected, `decode ${ fixture.type }`);
}
}
});
});
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected);
it(`should encode ${ small }n`, () => {
assert.strictEqual(byteUtils.toHex(encode.encode(BigInt(small))), fixture.data, `encode ${ small }`);
});
}
}
});
});
+254
View File
@@ -0,0 +1,254 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '40',
expected: '',
type: 'bytes'
},
{
data: '41a1',
expected: 'a1',
type: 'bytes'
},
{
data: '5801a1',
expected: 'a1',
type: 'bytes',
strict: false
},
{
data: '58ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 8-bit length'
},
{
data: '5900ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 16-bit length',
strict: false
},
{
data: '5a000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 32-bit length',
strict: false
},
{
data: '5b00000000000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 64-bit length',
strict: false
}
];
(() => {
function rnd(length) {
return new Uint8Array(Array.from({ length }, () => Math.floor(Math.random() * 255)));
}
const expected16 = rnd(256);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('590100'),
...expected16
]),
expected: expected16,
type: 'bytes',
label: 'long bytes, 16-bit length strict-compat'
});
const expected32 = rnd(65536);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('5a00010000'),
...expected32
]),
expected: expected32,
type: 'bytes',
label: 'long bytes, 32-bit length strict-compat'
});
})();
describe('bytes', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
let actual = decode.decode(data);
assert.strictEqual(byteUtils.toHex(actual), byteUtils.toHex(byteUtils.fromHex(fixture.expected)), `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
actual = decode.decode(data, { strict: true });
assert.strictEqual(byteUtils.toHex(actual), byteUtils.toHex(byteUtils.fromHex(fixture.expected)), `decode ${ fixture.type } strict`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('5ba5f702b3a5f702b3000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe')), /CBOR decode error: 64-bit integer bytes lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large bytes ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
});
continue;
}
const data = byteUtils.fromHex(fixture.expected);
const expectedHex = byteUtils.toHex(fixture.data);
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(() => encode.encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/);
} else if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type }`);
}
});
}
});
describe('typedarrays', () => {
const cases = [
{
obj: Uint8Array.from([
1,
2,
3
]),
hex: '43010203'
},
{
obj: Uint8ClampedArray.from([
1,
2,
3
]),
hex: '43010203'
},
{
obj: Uint16Array.from([
1,
2,
3
]),
hex: '46010002000300'
},
{
obj: Uint32Array.from([
1,
2,
3
]),
hex: '4c010000000200000003000000'
},
{
obj: Int8Array.from([
1,
2,
-3
]),
hex: '430102fd'
},
{
obj: Int16Array.from([
1,
2,
-3
]),
hex: '4601000200fdff'
},
{
obj: Int32Array.from([
1,
2,
-3
]),
hex: '4c0100000002000000fdffffff'
},
{
obj: Float32Array.from([
1,
2,
-3
]),
hex: '4c0000803f00000040000040c0'
},
{
obj: Float64Array.from([
1,
2,
-3
]),
hex: '5818000000000000f03f000000000000004000000000000008c0'
},
{
obj: BigUint64Array.from([
BigInt(1),
BigInt(2),
BigInt(3)
]),
hex: '5818010000000000000002000000000000000300000000000000'
},
{
obj: BigInt64Array.from([
BigInt(1),
BigInt(2),
BigInt(-3)
]),
hex: '581801000000000000000200000000000000fdffffffffffffff'
},
{
obj: new DataView(Uint8Array.from([
1,
2,
3
]).buffer),
hex: '43010203'
},
{
obj: Uint8Array.from([
1,
2,
3
]).buffer,
hex: '43010203'
}
];
for (const testCase of cases) {
it(testCase.obj.constructor.name, () => {
assert.equal(byteUtils.toHex(encode.encode(testCase.obj)), testCase.hex);
const decoded = decode.decode(byteUtils.fromHex(testCase.hex));
assert.instanceOf(decoded, Uint8Array);
assert.equal(byteUtils.toHex(decoded), byteUtils.toHex(testCase.obj));
});
}
});
if (byteUtils.useBuffer) {
describe('buffer', () => {
it('can encode Node.js Buffers', () => {
const obj = global.Buffer.from([
1,
2,
3
]);
assert.equal(byteUtils.toHex(encode.encode(obj)), '43010203');
const decoded = decode.decode(byteUtils.fromHex('43010203'));
assert.instanceOf(decoded, Uint8Array);
assert.equal(byteUtils.toHex(decoded), byteUtils.toHex(obj));
});
});
}
});
+144
View File
@@ -0,0 +1,144 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '60',
expected: '',
type: 'string'
},
{
data: '6161',
expected: 'a',
type: 'string'
},
{
data: '780161',
expected: 'a',
type: 'string',
strict: false
},
{
data: '6c48656c6c6f20776f726c6421',
expected: 'Hello world!',
type: 'string'
},
{
data: '6fc48c6175657320c39f76c49b746521',
expected: 'Čaues ßvěte!',
type: 'string'
},
{
data: '78964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 8-bit length'
},
{
data: '7900964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 16-bit length',
strict: false
},
{
data: '7a000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 32-bit length',
strict: false
},
{
data: '7b00000000000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 64-bit length',
strict: false
}
];
(() => {
function rnd(length) {
const sa = [];
let l = 0;
while (l < length) {
const ascii = length - l < 3;
const base = ascii ? 32 : 126976;
const max = ascii ? 126 : 130816;
const cc = Math.floor(Math.random() * (max - base)) + base;
const s = String.fromCharCode(cc);
l += new TextEncoder().encode(s).length;
sa.push(s);
}
return sa.join('');
}
const expected16 = rnd(256);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('790100'),
...new TextEncoder().encode(expected16)
]),
expected: expected16,
type: 'string',
label: 'long string, 16-bit length strict-compat'
});
const expected32 = rnd(65536);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('7a00010000'),
...new TextEncoder().encode(expected32)
]),
expected: expected32,
type: 'string',
label: 'long string, 32-bit length strict-compat'
});
})();
describe('string', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
let actual = decode.decode(data);
assert.strictEqual(actual, fixture.expected, `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
actual = decode.decode(data, { strict: true });
assert.strictEqual(actual, fixture.expected, `decode ${ fixture.type } strict`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('7ba5f702b3a5f702b34c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e')), /CBOR decode error: 64-bit integer string lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large string ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
});
continue;
}
const data = fixture.expected;
const expectedHex = byteUtils.toHex(fixture.data);
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(() => encode.encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/);
} else if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type }`);
}
});
}
});
});
+200
View File
@@ -0,0 +1,200 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '80',
expected: [],
type: 'array empty'
},
{
data: '8102',
expected: [2],
type: 'array 1 compact uint'
},
{
data: '8118ff',
expected: [255],
type: 'array 1 uint8'
},
{
data: '811901f4',
expected: [500],
type: 'array 1 uint16'
},
{
data: '811a00010000',
expected: [65536],
type: 'array 1 uint32'
},
{
data: '811b00000000000000ff',
expected: [255],
type: 'array 1 uint64',
strict: false
},
{
data: '811b0016db6db6db6db7',
expected: [Number.MAX_SAFE_INTEGER / 1.4],
type: 'array 1 uint64'
},
{
data: '811b001fffffffffffff',
expected: [Number.MAX_SAFE_INTEGER],
type: 'array 1 uint64'
},
{
data: '8403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints'
},
{
data: '8c1b0016db6db6db6db71a000100001901f40200202238ff3aa5f702b33b0016db6db6db6db74261316fc48c6175657320c39f76c49b746521',
expected: [
Number.MAX_SAFE_INTEGER / 1.4,
65536,
500,
2,
0,
-1,
-3,
-256,
-2784428724,
Number.MIN_SAFE_INTEGER / 1.4 - 1,
new TextEncoder().encode('a1'),
'Čaues ßvěte!'
],
type: 'array mixed terminals',
label: '[]'
},
{
data: '8265617272617982626f66820582666e657374656482666172726179736121',
expected: [
'array',
[
'of',
[
5,
[
'nested',
[
'arrays',
'!'
]
]
]
]
],
type: 'array nested'
},
{
data: '980403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length8',
strict: false
},
{
data: '99000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length16',
strict: false
},
{
data: '9a0000000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length32',
strict: false
},
{
data: '9b000000000000000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length64',
strict: false
}
];
describe('array', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(data), fixture.expected, `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.deepStrictEqual(decode.decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('9ba5f702b3a5f7020403040506')), /CBOR decode error: 64-bit integer array lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(encode.encode.bind(null, fixture.expected), Error, /^CBOR encode error: number too large to encode \(\d+\)$/);
} else if (fixture.strict === false) {
assert.notDeepEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(encode.encode(fixture.expected)), fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('9f616f6174ff')), [
'o',
't'
]);
});
it('can switch off indefinite length support', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('9f616f6174ff'), { allowIndefinite: false }), /indefinite/);
});
});
});
+667
View File
@@ -0,0 +1,667 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: 'a0',
expected: {},
type: 'map empty'
},
{
data: 'a0',
expected: new Map(),
type: 'map empty (useMaps)',
useMaps: true
},
{
data: 'a1616101',
expected: { a: 1 },
type: 'map 1 pair'
},
{
data: 'a161316161',
expected: { 1: 'a' },
type: 'map 1 pair (rev)'
},
{
data: 'a1016161',
expected: toMap([[
1,
'a'
]]),
type: 'map 1 pair (int key as Map w/ useMaps)',
useMaps: true
},
{
data: 'a243010203633132334302030463323334',
expected: toMap([
[
Uint8Array.from([
1,
2,
3
]),
'123'
],
[
Uint8Array.from([
2,
3,
4
]),
'234'
]
]),
type: 'map 2 pair (bytes keys Map w/ useMaps)',
useMaps: true
},
{
data: 'a1666f626a656374a16477697468a26134666e6573746564676f626a65637473a161216121',
expected: {
object: {
with: {
4: 'nested',
objects: { '!': '!' }
}
}
},
type: 'map nested'
},
{
data: 'a1666f626a656374a16477697468a204666e6573746564676f626a65637473a161216121',
expected: toMap([[
'object',
toMap([[
'with',
toMap([
[
4,
'nested'
],
[
'objects',
toMap([[
'!',
'!'
]])
]
])
]])
]]),
type: 'map nested w/ useMaps',
useMaps: true
},
{
data: 'ae636f6e651b0016db6db6db6db763736978206374656e3b0016db6db6db6db76374776f1a0001000064666976650064666f757202646e696e653aa5f702b365656967687438ff65736576656e226574687265651901f466656c6576656e426131667477656c76656fc48c6175657320c39f76c49b74652168666f75727465656ea4616664666f7572616f016174026274680368746869727465656e840203046466697665',
encode: {
one: Number.MAX_SAFE_INTEGER / 1.4,
two: 65536,
three: 500,
four: 2,
five: 0,
six: -1,
seven: -3,
eight: -256,
nine: -2784428724,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
thirteen: [
2,
3,
4,
'five'
],
fourteen: {
o: 1,
t: 2,
th: 3,
f: 'four'
}
},
expected: {
one: Number.MAX_SAFE_INTEGER / 1.4,
six: -1,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
two: 65536,
five: 0,
four: 2,
nine: -2784428724,
eight: -256,
seven: -3,
three: 500,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
fourteen: {
f: 'four',
o: 1,
t: 2,
th: 3
},
thirteen: [
2,
3,
4,
'five'
]
},
type: 'map with complex entries',
label: '{}'
},
{
data: 'ad01636f6e65026374776f1901f46c666976652068756e647265641902586b7369782068756e647265641a00010000636269671b0016db6db6db6db76662696767657220696d696e7573206f6e6521696d696e75732074776f38ff781f6d696e75732074776f2068756e6472656420616e64206669667479207369783901f4781a6d696e757820666976652068756e6472656420616e64206f6e653901f5781a6d696e757820666976652068756e6472656420616e642074776f3aa5f702b367626967206e65673b0016db6db6db6db76a626967676572206e6567',
encode: toMap([
[
2,
'two'
],
[
1,
'one'
],
[
-2,
'minus two'
],
[
-1,
'minus one'
],
[
600,
'six hundred'
],
[
500,
'five hundred'
],
[
-256,
'minus two hundred and fifty six'
],
[
-502,
'minux five hundred and two'
],
[
-501,
'minux five hundred and one'
],
[
65536,
'big'
],
[
-2784428724,
'big neg'
],
[
6433713753386423,
'bigger'
],
[
-6433713753386424,
'bigger neg'
]
]),
expected: toMap([
[
1,
'one'
],
[
2,
'two'
],
[
500,
'five hundred'
],
[
600,
'six hundred'
],
[
65536,
'big'
],
[
6433713753386423,
'bigger'
],
[
-1,
'minus one'
],
[
-2,
'minus two'
],
[
-256,
'minus two hundred and fifty six'
],
[
-501,
'minux five hundred and one'
],
[
-502,
'minux five hundred and two'
],
[
-2784428724,
'big neg'
],
[
-6433713753386424,
'bigger neg'
]
]),
type: 'map with ints and negints',
useMaps: true
},
{
data: 'a44104636f6e65430102026374776f430102036574687265654301020464666f7572',
encode: toMap([
[
Uint8Array.from([
1,
2,
3
]),
'three'
],
[
Uint8Array.from([4]),
'one'
],
[
Uint8Array.from([
1,
2,
4
]),
'four'
],
[
Uint8Array.from([
1,
2,
2
]),
'two'
]
]),
expected: toMap([
[
Uint8Array.from([4]),
'one'
],
[
Uint8Array.from([
1,
2,
2
]),
'two'
],
[
Uint8Array.from([
1,
2,
3
]),
'three'
],
[
Uint8Array.from([
1,
2,
4
]),
'four'
]
]),
type: 'map with bytes keys',
useMaps: true
},
{
data: 'b801616101',
expected: { a: 1 },
type: 'map 1 pair, length8',
strict: false
},
{
data: 'b90001616101',
expected: { a: 1 },
type: 'map 1 pair, length16',
strict: false
},
{
data: 'ba00000001616101',
expected: { a: 1 },
type: 'map 1 pair, length32',
strict: false
},
{
data: 'bb0000000000000001616101',
expected: { a: 1 },
type: 'map 1 pair, length64',
strict: false
}
];
function toMap(arr) {
const m = new Map();
for (const [key, value] of arr) {
m.set(key, value);
}
return m;
}
function entries(map) {
function nest(a) {
for (const e of a) {
e[0] = entries(e[0]);
e[1] = entries(e[1]);
}
return a;
}
if (Object.getPrototypeOf(map) === Map.prototype) {
return nest([...map.entries()]);
}
if (typeof map === 'object') {
return nest([...Object.entries(map)]);
}
return map;
}
describe('map', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
let options = fixture.useMaps ? { useMaps: true } : undefined;
const decoded = decode.decode(data, options);
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(decoded), Map.prototype, 'is Map');
} else {
assert.isObject(decoded, 'is object');
}
assert.deepStrictEqual(entries(decoded), entries(fixture.expected), `decode ${ fixture.type }`);
options = Object.assign({ strict: true }, options);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, options), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.deepStrictEqual(entries(decode.decode(data, options)), entries(fixture.expected), `decode ${ fixture.type }`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('bba5f702b3a5f70201616101')), /CBOR decode error: 64-bit integer map lengths not supported/);
});
}
it('errors', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a1016161')), /non-string keys not supported \(got number\)/);
});
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
const toEncode = fixture.encode || fixture.expected;
if (fixture.unsafe) {
assert.throws(encode.encode.bind(null, toEncode), Error, /^CBOR encode error: number too large to encode \(\d+\)$/);
} else if (fixture.strict === false || fixture.roundtrip === false) {
assert.notDeepEqual(byteUtils.toHex(encode.encode(toEncode)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(toEncode)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false && fixture.roundtrip !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
const toEncode = fixture.encode || fixture.expected;
const options = fixture.useMaps ? { useMaps: true } : undefined;
const rt = decode.decode(encode.encode(toEncode), options);
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(rt), Map.prototype, 'is Map');
} else {
assert.isObject(rt, 'is object');
}
assert.deepStrictEqual(entries(rt), entries(fixture.expected), `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('bf616f01617402ff')), {
o: 1,
t: 2
});
});
it('can switch off indefinite length support', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('bf616f01617402ff'), { allowIndefinite: false }), /indefinite/);
});
});
describe('sorting', () => {
it('sorts int map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
1,
1
],
[
2,
2
]
]))), 'a201010202');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
2,
1
],
[
1,
2
]
]))), 'a201020201');
});
it('sorts negint map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
-1,
1
],
[
-2,
2
]
]))), 'a220012102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
-2,
1
],
[
-1,
2
]
]))), 'a220022101');
});
it('sorts bytes map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
]
]))), 'a24201020142020102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
2,
1
]),
1
],
[
Uint8Array.from([
1,
2
]),
2
]
]))), 'a24201020242020101');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
],
[
Uint8Array.from([200]),
3
]
]))), 'a341c8034201020142020102');
});
it('sorts bytes map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
]
]))), 'a24201020142020102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
2,
1
]),
1
],
[
Uint8Array.from([
1,
2
]),
2
]
]))), 'a24201020242020101');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
],
[
Uint8Array.from([200]),
3
]
]))), 'a341c8034201020142020102');
});
it('sorts array map keys (length only)', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
[1],
1
],
[
[
1,
1
],
2
]
]))), 'a281010182010102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
[
1,
1
],
1
],
[
[1],
2
]
]))), 'a281010282010101');
});
it('sorts map map keys (length only)', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
{ a: 1 },
1
],
[
{
a: 1,
b: 1
},
2
]
]))), 'a2a161610101a261610161620102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
{
a: 1,
b: 1
},
1
],
[
{ a: 1 },
2
]
]))), 'a2a161610102a261610161620101');
});
});
});
+75
View File
@@ -0,0 +1,75 @@
'use strict';
var chai = require('chai');
var token = require('../lib/token.js');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var common = require('./common.js');
var encode = require('../lib/encode.js');
var decode = require('../lib/decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
function Uint16ArrayDecoder(obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 23');
}
const u8a = byteUtils.fromHex(obj);
return new Uint16Array(u8a.buffer, u8a.byteOffset, u8a.length / 2);
}
function Uint16ArrayEncoder(obj) {
if (!(obj instanceof Uint16Array)) {
throw new Error('expected Uint16Array for "Uint16Array" encoder');
}
return [
new token.Token(token.Type.tag, 23),
new token.Token(token.Type.string, byteUtils.toHex(obj))
];
}
describe('tag', () => {
it('date', () => {
assert.throws(() => encode.encode({ d: new Date() }), /unsupported type: Date/);
assert.equal(byteUtils.toHex(encode.encode(new Date('2013-03-21T20:04:00Z'), { typeEncoders: { Date: common.dateEncoder } })), 'c074323031332d30332d32315432303a30343a30305a');
const decodedDate = decode.decode(byteUtils.fromHex('c074323031332d30332d32315432303a30343a30305a'), { tags: { 0: common.dateDecoder } });
assert.instanceOf(decodedDate, Date);
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString());
});
it('Uint16Array as hex/23 (overide existing type)', () => {
assert.equal(byteUtils.toHex(encode.encode(Uint16Array.from([
1,
2,
3
]), { typeEncoders: { Uint16Array: Uint16ArrayEncoder } })), 'd76c303130303032303030333030');
const decoded = decode.decode(byteUtils.fromHex('d76c303130303032303030333030'), { tags: { 23: Uint16ArrayDecoder } });
assert.instanceOf(decoded, Uint16Array);
assert.equal(byteUtils.toHex(decoded), byteUtils.toHex(Uint16Array.from([
1,
2,
3
])));
});
it('tag int too large', () => {
const verify = (hex, strict) => {
if (!strict) {
assert.throws(() => decode.decode(byteUtils.fromHex(hex), {
tags: { 8: common.dateDecoder },
strict: true
}), /integer encoded in more bytes than necessary/);
}
const decodedDate = decode.decode(byteUtils.fromHex(hex), {
tags: { 8: common.dateDecoder },
strict
});
assert.instanceOf(decodedDate, Date);
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString());
};
verify('c874323031332d30332d32315432303a30343a30305a', true);
verify('d80874323031332d30332d32315432303a30343a30305a', false);
verify('d9000874323031332d30332d32315432303a30343a30305a', false);
verify('da0000000874323031332d30332d32315432303a30343a30305a', false);
verify('db000000000000000874323031332d30332d32315432303a30343a30305a', false);
});
});
+253
View File
@@ -0,0 +1,253 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '8601f5f4f6f720',
expected: [
1,
true,
false,
null,
undefined,
-1
],
type: 'array of float specials'
},
{
data: 'f93800',
expected: 0.5,
type: 'float16'
},
{
data: 'f9b800',
expected: -0.5,
type: 'float16'
},
{
data: 'fa33c00000',
expected: 8.940696716308594e-8,
type: 'float32'
},
{
data: 'fab3c00000',
expected: -8.940696716308594e-8,
type: 'float32'
},
{
data: 'fb3ff199999999999a',
expected: 1.1,
type: 'float64'
},
{
data: 'fbbff199999999999a',
expected: -1.1,
type: 'float64'
},
{
data: 'fb3ff1c71c71c71c72',
expected: 1.1111111111111112,
type: 'float64'
},
{
data: 'fb0000000000000002',
expected: 1e-323,
type: 'float64'
},
{
data: 'fb8000000000000002',
expected: -1e-323,
type: 'float64'
},
{
data: 'fb3fefffffffffffff',
expected: 0.9999999999999999,
type: 'float64'
},
{
data: 'fbbfefffffffffffff',
expected: -0.9999999999999999,
type: 'float64'
},
{
data: 'f97c00',
expected: Infinity,
type: 'Infinity'
},
{
data: 'fb7ff0000000000000',
expected: Infinity,
type: 'Infinity',
strict: false
},
{
data: 'f9fc00',
expected: -Infinity,
type: '-Infinity'
},
{
data: 'fbfff0000000000000',
expected: -Infinity,
type: '-Infinity',
strict: false
},
{
data: 'f97e00',
expected: NaN,
type: 'NaN'
},
{
data: 'f97ff8',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fa7ff80000',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb7ff8000000000000',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb7ff8cafedeadbeef',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb40f4241a31a5a515',
expected: 82497.63712086187,
type: 'float64'
}
];
describe('float', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(data), fixture.expected, `decode ${ fixture.type }`);
assert.deepStrictEqual(decode.decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
});
}
});
it('error', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('f80000')), Error, 'simple values are not supported');
assert.throws(() => decode.decode(byteUtils.fromHex('f900')), Error, 'not enough data for float16');
assert.throws(() => decode.decode(byteUtils.fromHex('fa0000')), Error, 'not enough data for float32');
assert.throws(() => decode.decode(byteUtils.fromHex('fb00000000')), Error, 'not enough data for float64');
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
});
}
}
});
describe('encode float64', () => {
for (const fixture of fixtures) {
if (fixture.type.startsWith('float')) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
const encoded = encode.encode(fixture.expected, { float64: true });
assert.strictEqual(encoded.length, 9);
assert.strictEqual(encoded[0], 251);
assert.strictEqual(decode.decode(encoded), fixture.expected, `encode float64 ${ fixture.type }`);
});
}
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(encode.encode(fixture.expected)), fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('indefinite length switch fails on BREAK', () => {
assert.throws(() => decode.decode(Uint8Array.from([
131,
1,
2,
255
])), /unexpected break to lengthed array/);
assert.throws(() => decode.decode(Uint8Array.from([
131,
1,
2,
255
]), { allowIndefinite: false }), /indefinite/);
});
it('can switch off undefined support', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7')), undefined);
assert.throws(() => decode.decode(byteUtils.fromHex('f7'), { allowUndefined: false }), /undefined/);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7')), [
1,
2,
undefined
]);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f7'), { allowUndefined: false }), /undefined/);
});
it('can coerce undefined to null', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7'), { coerceUndefinedToNull: false }), undefined);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7'), { coerceUndefinedToNull: true }), null);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7'), { coerceUndefinedToNull: false }), [
1,
2,
undefined
]);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7'), { coerceUndefinedToNull: true }), [
1,
2,
null
]);
});
it('can switch off Infinity support', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f97c00')), [
1,
2,
Infinity
]);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f9fc00')), [
1,
2,
-Infinity
]);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f97c00'), { allowInfinity: false }), /Infinity/);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f9fc00'), { allowInfinity: false }), /Infinity/);
for (const fixture of fixtures.filter(f => f.type.endsWith('Infinity'))) {
assert.throws(() => decode.decode(byteUtils.fromHex(fixture.data), { allowInfinity: false }), /Infinity/);
}
});
it('can switch off NaN support', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f97e00')), [
1,
2,
NaN
]);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f97e00'), { allowNaN: false }), /NaN/);
for (const fixture of fixtures.filter(f => f.type === 'NaN')) {
assert.throws(() => decode.decode(byteUtils.fromHex(fixture.data), { allowNaN: false }), /NaN/);
}
});
});
});
+91
View File
@@ -0,0 +1,91 @@
'use strict';
var chai = require('chai');
var bl = require('../lib/bl.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
describe('Internal bytes list', () => {
describe('push', () => {
it('push bits', () => {
const bl$1 = new bl.Bl(10);
const expected = [];
for (let i = 0; i < 25; i++) {
bl$1.push([i + 1]);
expected.push(i + 1);
}
assert.deepEqual([...bl$1.toBytes()], expected);
});
for (let i = 4; i < 21; i++) {
it(`push Bl(${ i })`, () => {
const bl$1 = new bl.Bl(i);
const expected = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
100,
110,
120,
11,
12,
130,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
];
for (let i = 0; i < 5; i++) {
bl$1.push([i + 1]);
}
bl$1.push(Uint8Array.from([
6,
7,
8,
9,
10
]));
bl$1.push([100]);
bl$1.push(Uint8Array.from([
110,
120
]));
bl$1.push(Uint8Array.from([
11,
12
]));
bl$1.push([130]);
bl$1.push(Uint8Array.from([
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]));
assert.deepEqual([...bl$1.toBytes()], expected);
});
}
});
});
@@ -0,0 +1,98 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var taglib = require('../taglib.js');
var byteUtils = require('../lib/byte-utils.js');
var appendix_a = require('./appendix_a.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const tags = [];
const typeEncoders = {};
tags[0] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1');
}
return `0("${ new Date(obj).toISOString().replace(/\.000Z$/, 'Z') }")`;
};
tags[1] = function (obj) {
if (typeof obj !== 'number') {
throw new Error('expected number for tag 1');
}
return `1(${ obj })`;
};
tags[2] = taglib.bigIntDecoder;
typeEncoders.bigint = taglib.bigIntEncoder;
tags[3] = taglib.bigNegIntDecoder;
tags[23] = function (obj) {
if (!(obj instanceof Uint8Array)) {
throw new Error('expected byte array for tag 23');
}
return `23(h'${ byteUtils.toHex(obj) }')`;
};
tags[24] = function (obj) {
return tags[23](obj).replace(/^23/, '24');
};
tags[32] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 32');
}
;
(() => new URL(obj))();
return `32("${ obj }")`;
};
describe('cbor/test-vectors', () => {
let i = 0;
for (const fixture of appendix_a.fixtures) {
const u8a = byteUtils.fromHex(fixture.hex);
let expected = fixture.decoded !== undefined ? fixture.decoded : fixture.diagnostic;
if (typeof expected === 'string' && expected.startsWith('h\'')) {
expected = byteUtils.fromHex(expected.replace(/(^h)'|('$)/g, ''));
}
it(`test vector #${ i }: ${ inspect(expected).replace(/\n\s*/g, '') }`, () => {
if (fixture.error) {
assert.throws(() => decode.decode(u8a, { tags }), fixture.error);
} else {
if (fixture.noTagDecodeError) {
assert.throws(() => decode.decode(u8a), fixture.noTagDecodeError);
}
let actual = decode.decode(u8a, { tags });
if (typeof actual === 'bigint') {
actual = inspect(actual);
}
if (typeof expected === 'bigint') {
expected = inspect(expected);
}
assert.deepEqual(actual, expected);
if (fixture.roundtrip) {
if (fixture.noTagEncodeError) {
assert.throws(() => encode.encode(decode.decode(u8a, { tags })), fixture.noTagEncodeError);
}
const reencoded = encode.encode(decode.decode(u8a, { tags }), { typeEncoders });
assert.equal(byteUtils.toHex(reencoded), fixture.hex);
}
}
});
i++;
}
it.skip('encode w/ tags', () => {
});
});
function inspect(o) {
if (typeof o === 'string') {
return `'${ o }'`;
}
if (o instanceof Uint8Array) {
return `Uint8Array<${ o.join(',') }>`;
}
if (o == null || typeof o !== 'object') {
return String(o);
}
return JSON.stringify(o);
}
@@ -0,0 +1,77 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
describe('decode errors', () => {
it('not Uint8Array', () => {
for (const arg of [
true,
false,
null,
undefined,
'string',
{ obj: 'ect' },
{},
['array'],
[],
[
1,
2,
3
],
0,
100,
1.1,
-1,
Symbol.for('nope')
]) {
assert.throws(() => decode.decode(arg), /CBOR decode error.*must be a Uint8Array/);
}
});
it('no data', () => {
assert.throws(() => decode.decode(new Uint8Array('')), /CBOR decode error.*content/);
});
it('break only', () => {
assert.throws(() => decode.decode(new Uint8Array([255])), /CBOR decode error.*break/);
});
it('not enough map entries (value)', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a2616f016174')), /map.*not enough entries.*value/);
});
it('not enough map entries (key)', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a2616f01')), /map.*not enough entries.*key/);
});
it('break in lengthed map', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a2616f01ff740f')), /unexpected break to lengthed map/);
});
it('not enough array entries', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('82616f')), /array.*not enough entries/);
});
it('break in lengthed array', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('82ff')), /unexpected break to lengthed array/);
});
it('no such decoder', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('82ff')), /unexpected break to lengthed array/);
});
it('too many terminals', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('0101')), /too many terminals/);
});
it('rejectDuplicateMapKeys enabled on duplicate keys', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('a3636261720363666f6f0163666f6f02')), {
foo: 2,
bar: 3
});
assert.throws(() => decode.decode(byteUtils.fromHex('a3636261720363666f6f0163666f6f02'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/);
assert.throws(() => decode.decode(byteUtils.fromHex('a3636261720363666f6f0163666f6f02'), {
useMaps: true,
rejectDuplicateMapKeys: true
}), /CBOR decode error: found repeat map key "foo"/);
});
});
+56
View File
@@ -0,0 +1,56 @@
'use strict';
var ipldGarbage = require('ipld-garbage');
require('../cborg.js');
var chai = require('chai');
var encode = require('../lib/encode.js');
var decode = require('../lib/decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
describe('Fuzz round-trip', () => {
it('random objects', function () {
this.timeout(5000);
for (let i = 0; i < 1000; i++) {
const obj = ipldGarbage.garbage(300, { weights: { CID: 0 } });
const byts = encode.encode(obj);
const decoded = decode.decode(byts);
assert.deepEqual(decoded, obj);
}
});
it('circular references error', () => {
let obj = {};
obj.obj = obj;
assert.throws(() => encode.encode(obj), /circular references/);
obj = {
blip: [
1,
2,
{ blop: {} }
]
};
obj.blip[2].blop.boop = obj;
assert.throws(() => encode.encode(obj), /circular references/);
obj = {
blip: [
1,
2,
{ blop: {} }
]
};
obj.blip[2].blop.boop = obj.blip;
assert.throws(() => encode.encode(obj), /circular references/);
obj = {
blip: {},
bloop: {}
};
obj.bloop = obj.blip;
assert.doesNotThrow(() => encode.encode(obj));
const arr = [];
arr[0] = arr;
assert.throws(() => encode.encode(arr), /circular references/);
});
});
+281
View File
@@ -0,0 +1,281 @@
'use strict';
var chai = require('chai');
require('../lib/json/json.js');
var encode = require('../lib/json/encode.js');
var decode = require('../lib/json/decode.js');
const toBytes = str => new TextEncoder().encode(str);
function verifyRoundTrip(obj, sorting) {
const encoded = new TextDecoder().decode(encode.encode(obj, sorting === false ? { mapSorter: null } : undefined));
const json = JSON.stringify(obj);
chai.assert.strictEqual(encoded, json);
const decoded = decode.decode(toBytes(JSON.stringify(obj)));
chai.assert.deepStrictEqual(decoded, obj);
}
function verifyEncodedForm(testCase) {
const obj = JSON.parse(testCase);
const encoded = encode.encode(obj);
chai.assert.strictEqual(new TextDecoder().decode(encoded), JSON.stringify(obj));
const decoded = decode.decode(encoded);
chai.assert.deepStrictEqual(decoded, obj);
const decoded2 = decode.decode(toBytes(testCase));
chai.assert.deepStrictEqual(decoded2, obj);
}
describe('json basics', () => {
it('can round-trip basic literals', () => {
const testCases = [
'null',
'true',
'false',
'0',
'9007199254740991',
'-9007199254740991',
JSON.stringify(Number.MAX_VALUE),
JSON.stringify(Number.MIN_VALUE)
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
chai.assert.strictEqual(decode.decode(toBytes('1E1')), 10);
chai.assert.strictEqual(decode.decode(toBytes('0.1e1')), 1);
chai.assert.strictEqual(decode.decode(toBytes('1e-1')), 0.1);
chai.assert.strictEqual(decode.decode(toBytes('1e+00')), 1);
chai.assert.strictEqual(decode.decode(toBytes('10.0')), 10);
chai.assert.deepStrictEqual(decode.decode(toBytes('[-10.0,1.0,0.0,100.0]')), [
-10,
1,
0,
100
]);
verifyRoundTrip(true);
verifyRoundTrip(false);
verifyRoundTrip(null);
verifyRoundTrip(100);
verifyRoundTrip(-100);
verifyRoundTrip(1.11);
verifyRoundTrip(-100.11111);
verifyRoundTrip(11100000000);
verifyRoundTrip(1.0011111e-18);
});
it('handles large integers as BigInt', () => {
const verify = (inp, str) => {
if (str === undefined) {
str = String(inp);
}
chai.assert.strictEqual(decode.decode(toBytes(str), { allowBigInt: true }), inp);
chai.assert.strictEqual(decode.decode(toBytes(str)), parseFloat(str));
};
verify(Number.MAX_SAFE_INTEGER);
verify(-Number.MAX_SAFE_INTEGER);
verify(BigInt('9007199254740992'));
verify(BigInt('9007199254740993'));
verify(BigInt('11959030306112471731'));
verify(BigInt('18446744073709551615'));
verify(BigInt('9223372036854775807'));
verify(BigInt('-9007199254740992'));
verify(BigInt('-9007199254740993'));
verify(BigInt('-9223372036854776000'));
verify(BigInt('-11959030306112471732'));
verify(BigInt('-18446744073709551616'));
verify(-9007199254740992, '-9007199254740992.0');
verify(-9223372036854776000, '-9223372036854776000.0');
verify(-18446744073709552000, '-18446744073709551616.0');
});
it('can round-trip string literals', () => {
const testCases = [
JSON.stringify(''),
JSON.stringify(' '),
JSON.stringify('"'),
JSON.stringify('\\'),
JSON.stringify('\b\f\n\r\t'),
JSON.stringify('"'),
JSON.stringify('&#34; %22 0x22 034 &#x22;'),
'"\uD83D\uDE00"'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
chai.assert.strictEqual(decode.decode(toBytes('"/ & \\/"')), '/ & /');
verifyRoundTrip('this is a string');
verifyRoundTrip('this \uD834\uDD1E is a \u263A\u263A \u2663 string ̐ ̀\n\r');
verifyRoundTrip('');
verifyRoundTrip('foo\\bar\nbaz\tbop\rbing"bip\'bang');
});
it('can round-trip array literals', () => {
const testCases = [
'[]',
'[null]',
'[true, false]',
'[ \n 0,1, 2\n , 3,\n4] \n ',
'[-10.0, 1.0, 0.0, 100.0]',
'[["2 deep"]]'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
verifyRoundTrip([
1,
2,
3,
'string',
true,
4
]);
verifyRoundTrip([
1,
2,
3,
'string',
true,
[
'and',
'a',
'nested',
'array',
true
],
4
]);
});
it('can round-trip object literals', () => {
const testCases = [
'{}',
'\n {\n "\\b"\n :\n""\n }\n ',
'{"":""}',
'{"1":{"2":0,"3":"deep"}}'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
});
it('will sort map keys', () => {
const unsorted = {
one: 1,
two: 2,
three: 3.1,
str: 'string',
bool: true,
four: 4
};
verifyRoundTrip(unsorted, false);
chai.assert.strictEqual(new TextDecoder().decode(encode.encode(unsorted)), '{"bool":true,"four":4,"one":1,"str":"string","three":3.1,"two":2}');
});
it('can handle novel cases', () => {
chai.assert.strictEqual(decode.decode(toBytes('"this \\uD834\\uDD1E is a \\u263a\\u263a string"')), 'this \uD834\uDD1E is a \u263A\u263A string');
verifyRoundTrip({
one: 1,
two: 2,
three: 3.1,
str: 'string',
arr: [
'and',
'a',
'nested',
[],
'array',
[
true,
1
],
false
],
bool: true,
obj: {
nested: 'object',
a: [],
o: {}
},
four: 4
}, false);
verifyRoundTrip([
false,
[
{
'#nFzU': {},
'\\w>': -0.9441451951197325,
'\t\'': '\'JB+2Wg\tw"IrM*#e^L/d&4rrzUuwq(1mH6aVRredB&Bfs]S"KqK(Tz1Q"URBAfw',
'\n@FrfM': 'M[D]q&'
},
'J4>\'Xdc+u2$%',
4227406737130333
]
], false);
verifyRoundTrip([
0.12995619865708727,
-4973404279772543,
{
drG2: [true],
';#K^Qf>V': null,
'`2=': 'ecc<e/$+-.;U>Gr5RdZDJ\n5+:{=QHNN.tVVN~dX$FWFwu`6>"&=tW!*1*^\u263A)JFM1p|}&X.B|${*\\f@!w2\u263A+'
}
], false);
chai.assert.strictEqual(`${ decode.decode(encode.encode(9007199254740991)) }`, '9007199254740991');
chai.assert.strictEqual(`${ decode.decode(encode.encode(9007199254740992)) }`, '9007199254740992');
chai.assert.strictEqual(`${ decode.decode(encode.encode(900719925474099100n)) }`, '900719925474099100');
});
it('should throw on bad types', () => {
chai.assert.throws(() => encode.encode(new Uint8Array([
1,
2
])), /CBOR encode error: unsupported type: Uint8Array/);
chai.assert.throws(() => encode.encode({
boop: new Uint8Array([
1,
2
])
}), /CBOR encode error: unsupported type: Uint8Array/);
chai.assert.throws(() => encode.encode(undefined), /CBOR encode error: unsupported type: undefined/);
chai.assert.throws(() => encode.encode(new Map([
[
1,
2
],
[
2,
3
]
])), /CBOR encode error: non-string map keys are not supported/);
chai.assert.throws(() => encode.encode(new Map([
[
[
'foo',
'bar'
],
2
],
[
[
'bar',
'foo'
],
3
]
])), /CBOR encode error: complex map keys are not supported/);
});
it('should throw on bad decode failure modes', () => {
chai.assert.throws(() => decode.decode(toBytes('{"a":1 & "b":2}')), 'CBOR decode error: unexpected character at position 7, was expecting object delimiter but found \'&\'');
chai.assert.throws(() => decode.decode(toBytes('{"a":1,"b"!2}')), 'CBOR decode error: unexpected character at position 10, was expecting key/value delimiter \':\' but found \'!\'');
chai.assert.throws(() => decode.decode(toBytes('[1,2&3]')), 'CBOR decode error: unexpected character at position 4, was expecting array delimiter but found \'&\'');
chai.assert.throws(() => decode.decode(toBytes('{"a":!}')), 'CBOR decode error: unexpected character at position 5');
chai.assert.throws(() => decode.decode(toBytes('"abc')), 'CBOR decode error: unexpected end of string at position 4');
chai.assert.throws(() => decode.decode(toBytes('"ab\\xc"')), 'CBOR decode error: unexpected string escape character at position 5');
chai.assert.throws(() => decode.decode(toBytes('"ab\x1Ec"')), 'CBOR decode error: invalid control character at position 3');
chai.assert.throws(() => decode.decode(toBytes('"ab\\')), 'CBOR decode error: unexpected string termination at position 4');
chai.assert.throws(() => decode.decode(toBytes('"\u263A').subarray(0, 3)), 'CBOR decode error: unexpected unicode sequence at position 1');
chai.assert.throws(() => decode.decode(toBytes('"\\uxyza"')), 'CBOR decode error: unexpected unicode escape character at position 3');
chai.assert.throws(() => decode.decode(toBytes('"\\u11"')), 'CBOR decode error: unexpected end of unicode escape sequence at position 3');
chai.assert.throws(() => decode.decode(toBytes('-boop')), 'CBOR decode error: unexpected token at position 1');
chai.assert.throws(() => decode.decode(toBytes('{"v":nope}')), 'CBOR decode error: unexpected token at position 7, expected to find \'null\'');
chai.assert.throws(() => decode.decode(toBytes('[n]')), 'CBOR decode error: unexpected end of input at position 1');
chai.assert.throws(() => decode.decode(toBytes('{"v":truu}')), 'CBOR decode error: unexpected token at position 9, expected to find \'true\'');
chai.assert.throws(() => decode.decode(toBytes('[tr]')), 'CBOR decode error: unexpected end of input at position 1');
chai.assert.throws(() => decode.decode(toBytes('{"v":flase}')), 'CBOR decode error: unexpected token at position 7, expected to find \'false\'');
chai.assert.throws(() => decode.decode(toBytes('[fa]')), 'CBOR decode error: unexpected end of input at position 1');
chai.assert.throws(() => decode.decode(toBytes('-0..1')), 'CBOR decode error: unexpected token at position 3');
});
it('should throw when rejectDuplicateMapKeys enabled on duplicate keys', () => {
chai.assert.deepStrictEqual(decode.decode(toBytes('{"foo":1,"foo":2}')), { foo: 2 });
chai.assert.throws(() => decode.decode(toBytes('{"foo":1,"foo":2}'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/);
});
});
+63
View File
@@ -0,0 +1,63 @@
'use strict';
var chai = require('chai');
var ipldGarbage = require('ipld-garbage');
var _0uint = require('../lib/0uint.js');
require('../cborg.js');
var length = require('../lib/length.js');
var common = require('./common.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
function verifyLength(object, options) {
const len = length.encodedLength(object, options);
const encoded = encode.encode(object, options);
const actual = encoded.length;
assert.strictEqual(actual, len, JSON.stringify(object));
}
describe('encodedLength', () => {
it('int boundaries', () => {
for (let ii = 0; ii < 4; ii++) {
verifyLength(_0uint.uintBoundaries[ii]);
verifyLength(_0uint.uintBoundaries[ii] - 1);
verifyLength(_0uint.uintBoundaries[ii] + 1);
verifyLength(-1 * _0uint.uintBoundaries[ii]);
verifyLength(-1 * _0uint.uintBoundaries[ii] - 1);
verifyLength(-1 * _0uint.uintBoundaries[ii] + 1);
}
});
it('tags', () => {
verifyLength({ date: new Date('2013-03-21T20:04:00Z') }, { typeEncoders: { Date: common.dateEncoder } });
});
it('floats', () => {
verifyLength(0.5);
verifyLength(0.5, { float64: true });
verifyLength(8.940696716308594e-8);
verifyLength(8.940696716308594e-8, { float64: true });
});
it('small garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 1000; ii++) {
const gbg = ipldGarbage.garbage(1 << 6, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
it('medium garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 100; ii++) {
const gbg = ipldGarbage.garbage(1 << 16, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
it('large garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 10; ii++) {
const gbg = ipldGarbage.garbage(1 << 20, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
});
+14
View File
@@ -0,0 +1,14 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var encode = require('./lib/encode.js');
var decode = require('./lib/decode.js');
var token = require('./lib/token.js');
exports.encode = encode.encode;
exports.decode = decode.decode;
exports.Token = token.Token;
exports.Type = token.Type;
+163
View File
@@ -0,0 +1,163 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var common = require('./common.js');
const uintBoundaries = [
24,
256,
65536,
4294967296,
BigInt('18446744073709551616')
];
function readUint8(data, offset, options) {
common.assertEnoughData(data, offset, 1);
const value = data[offset];
if (options.strict === true && value < uintBoundaries[0]) {
throw new Error(`${ common.decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
function readUint16(data, offset, options) {
common.assertEnoughData(data, offset, 2);
const value = data[offset] << 8 | data[offset + 1];
if (options.strict === true && value < uintBoundaries[1]) {
throw new Error(`${ common.decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
function readUint32(data, offset, options) {
common.assertEnoughData(data, offset, 4);
const value = data[offset] * 16777216 + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3];
if (options.strict === true && value < uintBoundaries[2]) {
throw new Error(`${ common.decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
function readUint64(data, offset, options) {
common.assertEnoughData(data, offset, 8);
const hi = data[offset] * 16777216 + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3];
const lo = data[offset + 4] * 16777216 + (data[offset + 5] << 16) + (data[offset + 6] << 8) + data[offset + 7];
const value = (BigInt(hi) << BigInt(32)) + BigInt(lo);
if (options.strict === true && value < uintBoundaries[3]) {
throw new Error(`${ common.decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
if (value <= Number.MAX_SAFE_INTEGER) {
return Number(value);
}
if (options.allowBigInt === true) {
return value;
}
throw new Error(`${ common.decodeErrPrefix } integers outside of the safe integer range are not supported`);
}
function decodeUint8(data, pos, _minor, options) {
return new token.Token(token.Type.uint, readUint8(data, pos + 1, options), 2);
}
function decodeUint16(data, pos, _minor, options) {
return new token.Token(token.Type.uint, readUint16(data, pos + 1, options), 3);
}
function decodeUint32(data, pos, _minor, options) {
return new token.Token(token.Type.uint, readUint32(data, pos + 1, options), 5);
}
function decodeUint64(data, pos, _minor, options) {
return new token.Token(token.Type.uint, readUint64(data, pos + 1, options), 9);
}
function encodeUint(buf, token) {
return encodeUintValue(buf, 0, token.value);
}
function encodeUintValue(buf, major, uint) {
if (uint < uintBoundaries[0]) {
const nuint = Number(uint);
buf.push([major | nuint]);
} else if (uint < uintBoundaries[1]) {
const nuint = Number(uint);
buf.push([
major | 24,
nuint
]);
} else if (uint < uintBoundaries[2]) {
const nuint = Number(uint);
buf.push([
major | 25,
nuint >>> 8,
nuint & 255
]);
} else if (uint < uintBoundaries[3]) {
const nuint = Number(uint);
buf.push([
major | 26,
nuint >>> 24 & 255,
nuint >>> 16 & 255,
nuint >>> 8 & 255,
nuint & 255
]);
} else {
const buint = BigInt(uint);
if (buint < uintBoundaries[4]) {
const set = [
major | 27,
0,
0,
0,
0,
0,
0,
0
];
let lo = Number(buint & BigInt(4294967295));
let hi = Number(buint >> BigInt(32) & BigInt(4294967295));
set[8] = lo & 255;
lo = lo >> 8;
set[7] = lo & 255;
lo = lo >> 8;
set[6] = lo & 255;
lo = lo >> 8;
set[5] = lo & 255;
set[4] = hi & 255;
hi = hi >> 8;
set[3] = hi & 255;
hi = hi >> 8;
set[2] = hi & 255;
hi = hi >> 8;
set[1] = hi & 255;
buf.push(set);
} else {
throw new Error(`${ common.decodeErrPrefix } encountered BigInt larger than allowable range`);
}
}
}
encodeUint.encodedSize = function encodedSize(token) {
return encodeUintValue.encodedSize(token.value);
};
encodeUintValue.encodedSize = function encodedSize(uint) {
if (uint < uintBoundaries[0]) {
return 1;
}
if (uint < uintBoundaries[1]) {
return 2;
}
if (uint < uintBoundaries[2]) {
return 3;
}
if (uint < uintBoundaries[3]) {
return 5;
}
return 9;
};
encodeUint.compareTokens = function compareTokens(tok1, tok2) {
return tok1.value < tok2.value ? -1 : tok1.value > tok2.value ? 1 : 0;
};
exports.decodeUint16 = decodeUint16;
exports.decodeUint32 = decodeUint32;
exports.decodeUint64 = decodeUint64;
exports.decodeUint8 = decodeUint8;
exports.encodeUint = encodeUint;
exports.encodeUintValue = encodeUintValue;
exports.readUint16 = readUint16;
exports.readUint32 = readUint32;
exports.readUint64 = readUint64;
exports.readUint8 = readUint8;
exports.uintBoundaries = uintBoundaries;
+63
View File
@@ -0,0 +1,63 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var _0uint = require('./0uint.js');
var common = require('./common.js');
function decodeNegint8(data, pos, _minor, options) {
return new token.Token(token.Type.negint, -1 - _0uint.readUint8(data, pos + 1, options), 2);
}
function decodeNegint16(data, pos, _minor, options) {
return new token.Token(token.Type.negint, -1 - _0uint.readUint16(data, pos + 1, options), 3);
}
function decodeNegint32(data, pos, _minor, options) {
return new token.Token(token.Type.negint, -1 - _0uint.readUint32(data, pos + 1, options), 5);
}
const neg1b = BigInt(-1);
const pos1b = BigInt(1);
function decodeNegint64(data, pos, _minor, options) {
const int = _0uint.readUint64(data, pos + 1, options);
if (typeof int !== 'bigint') {
const value = -1 - int;
if (value >= Number.MIN_SAFE_INTEGER) {
return new token.Token(token.Type.negint, value, 9);
}
}
if (options.allowBigInt !== true) {
throw new Error(`${ common.decodeErrPrefix } integers outside of the safe integer range are not supported`);
}
return new token.Token(token.Type.negint, neg1b - BigInt(int), 9);
}
function encodeNegint(buf, token) {
const negint = token.value;
const unsigned = typeof negint === 'bigint' ? negint * neg1b - pos1b : negint * -1 - 1;
_0uint.encodeUintValue(buf, token.type.majorEncoded, unsigned);
}
encodeNegint.encodedSize = function encodedSize(token) {
const negint = token.value;
const unsigned = typeof negint === 'bigint' ? negint * neg1b - pos1b : negint * -1 - 1;
if (unsigned < _0uint.uintBoundaries[0]) {
return 1;
}
if (unsigned < _0uint.uintBoundaries[1]) {
return 2;
}
if (unsigned < _0uint.uintBoundaries[2]) {
return 3;
}
if (unsigned < _0uint.uintBoundaries[3]) {
return 5;
}
return 9;
};
encodeNegint.compareTokens = function compareTokens(tok1, tok2) {
return tok1.value < tok2.value ? 1 : tok1.value > tok2.value ? -1 : 0;
};
exports.decodeNegint16 = decodeNegint16;
exports.decodeNegint32 = decodeNegint32;
exports.decodeNegint64 = decodeNegint64;
exports.decodeNegint8 = decodeNegint8;
exports.encodeNegint = encodeNegint;
+62
View File
@@ -0,0 +1,62 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var common = require('./common.js');
var _0uint = require('./0uint.js');
var byteUtils = require('./byte-utils.js');
function toToken(data, pos, prefix, length) {
common.assertEnoughData(data, pos, prefix + length);
const buf = byteUtils.slice(data, pos + prefix, pos + prefix + length);
return new token.Token(token.Type.bytes, buf, prefix + length);
}
function decodeBytesCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
function decodeBytes8(data, pos, _minor, options) {
return toToken(data, pos, 2, _0uint.readUint8(data, pos + 1, options));
}
function decodeBytes16(data, pos, _minor, options) {
return toToken(data, pos, 3, _0uint.readUint16(data, pos + 1, options));
}
function decodeBytes32(data, pos, _minor, options) {
return toToken(data, pos, 5, _0uint.readUint32(data, pos + 1, options));
}
function decodeBytes64(data, pos, _minor, options) {
const l = _0uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ common.decodeErrPrefix } 64-bit integer bytes lengths not supported`);
}
return toToken(data, pos, 9, l);
}
function tokenBytes(token$1) {
if (token$1.encodedBytes === undefined) {
token$1.encodedBytes = token$1.type === token.Type.string ? byteUtils.fromString(token$1.value) : token$1.value;
}
return token$1.encodedBytes;
}
function encodeBytes(buf, token) {
const bytes = tokenBytes(token);
_0uint.encodeUintValue(buf, token.type.majorEncoded, bytes.length);
buf.push(bytes);
}
encodeBytes.encodedSize = function encodedSize(token) {
const bytes = tokenBytes(token);
return _0uint.encodeUintValue.encodedSize(bytes.length) + bytes.length;
};
encodeBytes.compareTokens = function compareTokens(tok1, tok2) {
return compareBytes(tokenBytes(tok1), tokenBytes(tok2));
};
function compareBytes(b1, b2) {
return b1.length < b2.length ? -1 : b1.length > b2.length ? 1 : byteUtils.compare(b1, b2);
}
exports.compareBytes = compareBytes;
exports.decodeBytes16 = decodeBytes16;
exports.decodeBytes32 = decodeBytes32;
exports.decodeBytes64 = decodeBytes64;
exports.decodeBytes8 = decodeBytes8;
exports.decodeBytesCompact = decodeBytesCompact;
exports.encodeBytes = encodeBytes;
+46
View File
@@ -0,0 +1,46 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var common = require('./common.js');
var _0uint = require('./0uint.js');
var _2bytes = require('./2bytes.js');
var byteUtils = require('./byte-utils.js');
function toToken(data, pos, prefix, length, options) {
const totLength = prefix + length;
common.assertEnoughData(data, pos, totLength);
const tok = new token.Token(token.Type.string, byteUtils.toString(data, pos + prefix, pos + totLength), totLength);
if (options.retainStringBytes === true) {
tok.byteValue = byteUtils.slice(data, pos + prefix, pos + totLength);
}
return tok;
}
function decodeStringCompact(data, pos, minor, options) {
return toToken(data, pos, 1, minor, options);
}
function decodeString8(data, pos, _minor, options) {
return toToken(data, pos, 2, _0uint.readUint8(data, pos + 1, options), options);
}
function decodeString16(data, pos, _minor, options) {
return toToken(data, pos, 3, _0uint.readUint16(data, pos + 1, options), options);
}
function decodeString32(data, pos, _minor, options) {
return toToken(data, pos, 5, _0uint.readUint32(data, pos + 1, options), options);
}
function decodeString64(data, pos, _minor, options) {
const l = _0uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ common.decodeErrPrefix } 64-bit integer string lengths not supported`);
}
return toToken(data, pos, 9, l, options);
}
const encodeString = _2bytes.encodeBytes;
exports.decodeString16 = decodeString16;
exports.decodeString32 = decodeString32;
exports.decodeString64 = decodeString64;
exports.decodeString8 = decodeString8;
exports.decodeStringCompact = decodeStringCompact;
exports.encodeString = encodeString;
+51
View File
@@ -0,0 +1,51 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var _0uint = require('./0uint.js');
var common = require('./common.js');
function toToken(_data, _pos, prefix, length) {
return new token.Token(token.Type.array, length, prefix);
}
function decodeArrayCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
function decodeArray8(data, pos, _minor, options) {
return toToken(data, pos, 2, _0uint.readUint8(data, pos + 1, options));
}
function decodeArray16(data, pos, _minor, options) {
return toToken(data, pos, 3, _0uint.readUint16(data, pos + 1, options));
}
function decodeArray32(data, pos, _minor, options) {
return toToken(data, pos, 5, _0uint.readUint32(data, pos + 1, options));
}
function decodeArray64(data, pos, _minor, options) {
const l = _0uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ common.decodeErrPrefix } 64-bit integer array lengths not supported`);
}
return toToken(data, pos, 9, l);
}
function decodeArrayIndefinite(data, pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ common.decodeErrPrefix } indefinite length items not allowed`);
}
return toToken(data, pos, 1, Infinity);
}
function encodeArray(buf, token$1) {
_0uint.encodeUintValue(buf, token.Type.array.majorEncoded, token$1.value);
}
encodeArray.compareTokens = _0uint.encodeUint.compareTokens;
encodeArray.encodedSize = function encodedSize(token) {
return _0uint.encodeUintValue.encodedSize(token.value);
};
exports.decodeArray16 = decodeArray16;
exports.decodeArray32 = decodeArray32;
exports.decodeArray64 = decodeArray64;
exports.decodeArray8 = decodeArray8;
exports.decodeArrayCompact = decodeArrayCompact;
exports.decodeArrayIndefinite = decodeArrayIndefinite;
exports.encodeArray = encodeArray;
+51
View File
@@ -0,0 +1,51 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var _0uint = require('./0uint.js');
var common = require('./common.js');
function toToken(_data, _pos, prefix, length) {
return new token.Token(token.Type.map, length, prefix);
}
function decodeMapCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
function decodeMap8(data, pos, _minor, options) {
return toToken(data, pos, 2, _0uint.readUint8(data, pos + 1, options));
}
function decodeMap16(data, pos, _minor, options) {
return toToken(data, pos, 3, _0uint.readUint16(data, pos + 1, options));
}
function decodeMap32(data, pos, _minor, options) {
return toToken(data, pos, 5, _0uint.readUint32(data, pos + 1, options));
}
function decodeMap64(data, pos, _minor, options) {
const l = _0uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ common.decodeErrPrefix } 64-bit integer map lengths not supported`);
}
return toToken(data, pos, 9, l);
}
function decodeMapIndefinite(data, pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ common.decodeErrPrefix } indefinite length items not allowed`);
}
return toToken(data, pos, 1, Infinity);
}
function encodeMap(buf, token$1) {
_0uint.encodeUintValue(buf, token.Type.map.majorEncoded, token$1.value);
}
encodeMap.compareTokens = _0uint.encodeUint.compareTokens;
encodeMap.encodedSize = function encodedSize(token) {
return _0uint.encodeUintValue.encodedSize(token.value);
};
exports.decodeMap16 = decodeMap16;
exports.decodeMap32 = decodeMap32;
exports.decodeMap64 = decodeMap64;
exports.decodeMap8 = decodeMap8;
exports.decodeMapCompact = decodeMapCompact;
exports.decodeMapIndefinite = decodeMapIndefinite;
exports.encodeMap = encodeMap;
+36
View File
@@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var _0uint = require('./0uint.js');
function decodeTagCompact(_data, _pos, minor, _options) {
return new token.Token(token.Type.tag, minor, 1);
}
function decodeTag8(data, pos, _minor, options) {
return new token.Token(token.Type.tag, _0uint.readUint8(data, pos + 1, options), 2);
}
function decodeTag16(data, pos, _minor, options) {
return new token.Token(token.Type.tag, _0uint.readUint16(data, pos + 1, options), 3);
}
function decodeTag32(data, pos, _minor, options) {
return new token.Token(token.Type.tag, _0uint.readUint32(data, pos + 1, options), 5);
}
function decodeTag64(data, pos, _minor, options) {
return new token.Token(token.Type.tag, _0uint.readUint64(data, pos + 1, options), 9);
}
function encodeTag(buf, token$1) {
_0uint.encodeUintValue(buf, token.Type.tag.majorEncoded, token$1.value);
}
encodeTag.compareTokens = _0uint.encodeUint.compareTokens;
encodeTag.encodedSize = function encodedSize(token) {
return _0uint.encodeUintValue.encodedSize(token.value);
};
exports.decodeTag16 = decodeTag16;
exports.decodeTag32 = decodeTag32;
exports.decodeTag64 = decodeTag64;
exports.decodeTag8 = decodeTag8;
exports.decodeTagCompact = decodeTagCompact;
exports.encodeTag = encodeTag;
+188
View File
@@ -0,0 +1,188 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var common = require('./common.js');
var _0uint = require('./0uint.js');
const MINOR_FALSE = 20;
const MINOR_TRUE = 21;
const MINOR_NULL = 22;
const MINOR_UNDEFINED = 23;
function decodeUndefined(_data, _pos, _minor, options) {
if (options.allowUndefined === false) {
throw new Error(`${ common.decodeErrPrefix } undefined values are not supported`);
} else if (options.coerceUndefinedToNull === true) {
return new token.Token(token.Type.null, null, 1);
}
return new token.Token(token.Type.undefined, undefined, 1);
}
function decodeBreak(_data, _pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ common.decodeErrPrefix } indefinite length items not allowed`);
}
return new token.Token(token.Type.break, undefined, 1);
}
function createToken(value, bytes, options) {
if (options) {
if (options.allowNaN === false && Number.isNaN(value)) {
throw new Error(`${ common.decodeErrPrefix } NaN values are not supported`);
}
if (options.allowInfinity === false && (value === Infinity || value === -Infinity)) {
throw new Error(`${ common.decodeErrPrefix } Infinity values are not supported`);
}
}
return new token.Token(token.Type.float, value, bytes);
}
function decodeFloat16(data, pos, _minor, options) {
return createToken(readFloat16(data, pos + 1), 3, options);
}
function decodeFloat32(data, pos, _minor, options) {
return createToken(readFloat32(data, pos + 1), 5, options);
}
function decodeFloat64(data, pos, _minor, options) {
return createToken(readFloat64(data, pos + 1), 9, options);
}
function encodeFloat(buf, token$1, options) {
const float = token$1.value;
if (float === false) {
buf.push([token.Type.float.majorEncoded | MINOR_FALSE]);
} else if (float === true) {
buf.push([token.Type.float.majorEncoded | MINOR_TRUE]);
} else if (float === null) {
buf.push([token.Type.float.majorEncoded | MINOR_NULL]);
} else if (float === undefined) {
buf.push([token.Type.float.majorEncoded | MINOR_UNDEFINED]);
} else {
let decoded;
let success = false;
if (!options || options.float64 !== true) {
encodeFloat16(float);
decoded = readFloat16(ui8a, 1);
if (float === decoded || Number.isNaN(float)) {
ui8a[0] = 249;
buf.push(ui8a.slice(0, 3));
success = true;
} else {
encodeFloat32(float);
decoded = readFloat32(ui8a, 1);
if (float === decoded) {
ui8a[0] = 250;
buf.push(ui8a.slice(0, 5));
success = true;
}
}
}
if (!success) {
encodeFloat64(float);
decoded = readFloat64(ui8a, 1);
ui8a[0] = 251;
buf.push(ui8a.slice(0, 9));
}
}
}
encodeFloat.encodedSize = function encodedSize(token, options) {
const float = token.value;
if (float === false || float === true || float === null || float === undefined) {
return 1;
}
if (!options || options.float64 !== true) {
encodeFloat16(float);
let decoded = readFloat16(ui8a, 1);
if (float === decoded || Number.isNaN(float)) {
return 3;
}
encodeFloat32(float);
decoded = readFloat32(ui8a, 1);
if (float === decoded) {
return 5;
}
}
return 9;
};
const buffer = new ArrayBuffer(9);
const dataView = new DataView(buffer, 1);
const ui8a = new Uint8Array(buffer, 0);
function encodeFloat16(inp) {
if (inp === Infinity) {
dataView.setUint16(0, 31744, false);
} else if (inp === -Infinity) {
dataView.setUint16(0, 64512, false);
} else if (Number.isNaN(inp)) {
dataView.setUint16(0, 32256, false);
} else {
dataView.setFloat32(0, inp);
const valu32 = dataView.getUint32(0);
const exponent = (valu32 & 2139095040) >> 23;
const mantissa = valu32 & 8388607;
if (exponent === 255) {
dataView.setUint16(0, 31744, false);
} else if (exponent === 0) {
dataView.setUint16(0, (inp & 2147483648) >> 16 | mantissa >> 13, false);
} else {
const logicalExponent = exponent - 127;
if (logicalExponent < -24) {
dataView.setUint16(0, 0);
} else if (logicalExponent < -14) {
dataView.setUint16(0, (valu32 & 2147483648) >> 16 | 1 << 24 + logicalExponent, false);
} else {
dataView.setUint16(0, (valu32 & 2147483648) >> 16 | logicalExponent + 15 << 10 | mantissa >> 13, false);
}
}
}
}
function readFloat16(ui8a, pos) {
if (ui8a.length - pos < 2) {
throw new Error(`${ common.decodeErrPrefix } not enough data for float16`);
}
const half = (ui8a[pos] << 8) + ui8a[pos + 1];
if (half === 31744) {
return Infinity;
}
if (half === 64512) {
return -Infinity;
}
if (half === 32256) {
return NaN;
}
const exp = half >> 10 & 31;
const mant = half & 1023;
let val;
if (exp === 0) {
val = mant * 2 ** -24;
} else if (exp !== 31) {
val = (mant + 1024) * 2 ** (exp - 25);
} else {
val = mant === 0 ? Infinity : NaN;
}
return half & 32768 ? -val : val;
}
function encodeFloat32(inp) {
dataView.setFloat32(0, inp, false);
}
function readFloat32(ui8a, pos) {
if (ui8a.length - pos < 4) {
throw new Error(`${ common.decodeErrPrefix } not enough data for float32`);
}
const offset = (ui8a.byteOffset || 0) + pos;
return new DataView(ui8a.buffer, offset, 4).getFloat32(0, false);
}
function encodeFloat64(inp) {
dataView.setFloat64(0, inp, false);
}
function readFloat64(ui8a, pos) {
if (ui8a.length - pos < 8) {
throw new Error(`${ common.decodeErrPrefix } not enough data for float64`);
}
const offset = (ui8a.byteOffset || 0) + pos;
return new DataView(ui8a.buffer, offset, 8).getFloat64(0, false);
}
encodeFloat.compareTokens = _0uint.encodeUint.compareTokens;
exports.decodeBreak = decodeBreak;
exports.decodeFloat16 = decodeFloat16;
exports.decodeFloat32 = decodeFloat32;
exports.decodeFloat64 = decodeFloat64;
exports.decodeUndefined = decodeUndefined;
exports.encodeFloat = encodeFloat;
+139
View File
@@ -0,0 +1,139 @@
'use strict';
var process = require('process');
require('../cborg.js');
var diagnostic = require('./diagnostic.js');
var byteUtils = require('./byte-utils.js');
var encode = require('./encode.js');
var decode = require('./decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var process__default = /*#__PURE__*/_interopDefaultLegacy(process);
function usage(code) {
console.error('Usage: cborg <command> <args>');
console.error('Valid commands:');
console.error('\tbin2diag [binary input]');
console.error('\tbin2hex [binary input]');
console.error('\tbin2json [--pretty] [binary input]');
console.error('\tdiag2bin [diagnostic input]');
console.error('\tdiag2hex [diagnostic input]');
console.error('\tdiag2json [--pretty] [diagnostic input]');
console.error('\thex2bin [hex input]');
console.error('\thex2diag [hex input]');
console.error('\thex2json [--pretty] [hex input]');
console.error('\tjson2bin \'[json input]\'');
console.error('\tjson2diag \'[json input]\'');
console.error('\tjson2hex \'[json input]\'');
console.error('Input may either be supplied as an argument or piped via stdin');
process__default["default"].exit(code || 0);
}
async function fromStdin() {
const chunks = [];
for await (const chunk of process__default["default"].stdin) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
}
function fromHex(str) {
str = str.replace(/\r?\n/g, '');
if (!/^([0-9a-f]{2})*$/i.test(str)) {
throw new Error('Input string is not hexadecimal format');
}
return byteUtils.fromHex(str);
}
function argvPretty() {
const argv = process__default["default"].argv.filter(s => s !== '--pretty');
const pretty = argv.length !== process__default["default"].argv.length;
return {
argv,
pretty
};
}
async function run() {
const cmd = process__default["default"].argv[2];
switch (cmd) {
case 'help': {
return usage(0);
}
case 'bin2diag': {
const bin = process__default["default"].argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process__default["default"].argv[3]);
for (const line of diagnostic.tokensToDiagnostic(bin)) {
console.log(line);
}
return;
}
case 'bin2hex': {
const bin = process__default["default"].argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process__default["default"].argv[3]);
return console.log(byteUtils.toHex(bin));
}
case 'bin2json': {
const {argv, pretty} = argvPretty();
const bin = argv.length < 4 ? await fromStdin() : new TextEncoder().encode(argv[3]);
return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
}
case 'diag2bin': {
const bin = diagnostic.fromDiag(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
return process__default["default"].stdout.write(bin);
}
case 'diag2hex': {
const bin = diagnostic.fromDiag(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
return console.log(byteUtils.toHex(bin));
}
case 'diag2json': {
const {argv, pretty} = argvPretty();
const bin = diagnostic.fromDiag(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
}
case 'hex2bin': {
const bin = fromHex(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
return process__default["default"].stdout.write(bin);
}
case 'hex2diag': {
const bin = fromHex(process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3]);
for (const line of diagnostic.tokensToDiagnostic(bin)) {
console.log(line);
}
return;
}
case 'hex2json': {
const {argv, pretty} = argvPretty();
const bin = fromHex(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
return console.log(JSON.stringify(decode.decode(bin), undefined, pretty ? 2 : undefined));
}
case 'json2bin': {
const inp = process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3];
const obj = JSON.parse(inp);
return process__default["default"].stdout.write(encode.encode(obj));
}
case 'json2diag': {
const inp = process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3];
const obj = JSON.parse(inp);
for (const line of diagnostic.tokensToDiagnostic(encode.encode(obj))) {
console.log(line);
}
return;
}
case 'json2hex': {
const inp = process__default["default"].argv.length < 4 ? (await fromStdin()).toString() : process__default["default"].argv[3];
const obj = JSON.parse(inp);
return console.log(byteUtils.toHex(encode.encode(obj)));
}
default: {
if (process__default["default"].argv.findIndex(a => a.endsWith('mocha')) === -1) {
if (cmd) {
console.error(`Unknown command: '${ cmd }'`);
}
usage(1);
}
}
}
}
run().catch(err => {
console.error(err);
process__default["default"].exit(1);
});
var bin = true;
module.exports = bin;
+77
View File
@@ -0,0 +1,77 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var byteUtils = require('./byte-utils.js');
const defaultChunkSize = 256;
class Bl {
constructor(chunkSize = defaultChunkSize) {
this.chunkSize = chunkSize;
this.cursor = 0;
this.maxCursor = -1;
this.chunks = [];
this._initReuseChunk = null;
}
reset() {
this.cursor = 0;
this.maxCursor = -1;
if (this.chunks.length) {
this.chunks = [];
}
if (this._initReuseChunk !== null) {
this.chunks.push(this._initReuseChunk);
this.maxCursor = this._initReuseChunk.length - 1;
}
}
push(bytes) {
let topChunk = this.chunks[this.chunks.length - 1];
const newMax = this.cursor + bytes.length;
if (newMax <= this.maxCursor + 1) {
const chunkPos = topChunk.length - (this.maxCursor - this.cursor) - 1;
topChunk.set(bytes, chunkPos);
} else {
if (topChunk) {
const chunkPos = topChunk.length - (this.maxCursor - this.cursor) - 1;
if (chunkPos < topChunk.length) {
this.chunks[this.chunks.length - 1] = topChunk.subarray(0, chunkPos);
this.maxCursor = this.cursor - 1;
}
}
if (bytes.length < 64 && bytes.length < this.chunkSize) {
topChunk = byteUtils.alloc(this.chunkSize);
this.chunks.push(topChunk);
this.maxCursor += topChunk.length;
if (this._initReuseChunk === null) {
this._initReuseChunk = topChunk;
}
topChunk.set(bytes, 0);
} else {
this.chunks.push(bytes);
this.maxCursor += bytes.length;
}
}
this.cursor += bytes.length;
}
toBytes(reset = false) {
let byts;
if (this.chunks.length === 1) {
const chunk = this.chunks[0];
if (reset && this.cursor > chunk.length / 2) {
byts = this.cursor === chunk.length ? chunk : chunk.subarray(0, this.cursor);
this._initReuseChunk = null;
this.chunks = [];
} else {
byts = byteUtils.slice(chunk, 0, this.cursor);
}
} else {
byts = byteUtils.concat(this.chunks, this.cursor);
}
if (reset) {
this.reset();
}
return byts;
}
}
exports.Bl = Bl;
+245
View File
@@ -0,0 +1,245 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const useBuffer = globalThis.process && !globalThis.process.browser && globalThis.Buffer && typeof globalThis.Buffer.isBuffer === 'function';
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
function isBuffer(buf) {
return useBuffer && globalThis.Buffer.isBuffer(buf);
}
function asU8A(buf) {
if (!(buf instanceof Uint8Array)) {
return Uint8Array.from(buf);
}
return isBuffer(buf) ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : buf;
}
const toString = useBuffer ? (bytes, start, end) => {
return end - start > 64 ? globalThis.Buffer.from(bytes.subarray(start, end)).toString('utf8') : utf8Slice(bytes, start, end);
} : (bytes, start, end) => {
return end - start > 64 ? textDecoder.decode(bytes.subarray(start, end)) : utf8Slice(bytes, start, end);
};
const fromString = useBuffer ? string => {
return string.length > 64 ? globalThis.Buffer.from(string) : utf8ToBytes(string);
} : string => {
return string.length > 64 ? textEncoder.encode(string) : utf8ToBytes(string);
};
const fromArray = arr => {
return Uint8Array.from(arr);
};
const slice = useBuffer ? (bytes, start, end) => {
if (isBuffer(bytes)) {
return new Uint8Array(bytes.subarray(start, end));
}
return bytes.slice(start, end);
} : (bytes, start, end) => {
return bytes.slice(start, end);
};
const concat = useBuffer ? (chunks, length) => {
chunks = chunks.map(c => c instanceof Uint8Array ? c : globalThis.Buffer.from(c));
return asU8A(globalThis.Buffer.concat(chunks, length));
} : (chunks, length) => {
const out = new Uint8Array(length);
let off = 0;
for (let b of chunks) {
if (off + b.length > out.length) {
b = b.subarray(0, out.length - off);
}
out.set(b, off);
off += b.length;
}
return out;
};
const alloc = useBuffer ? size => {
return globalThis.Buffer.allocUnsafe(size);
} : size => {
return new Uint8Array(size);
};
const toHex = useBuffer ? d => {
if (typeof d === 'string') {
return d;
}
return globalThis.Buffer.from(toBytes(d)).toString('hex');
} : d => {
if (typeof d === 'string') {
return d;
}
return Array.prototype.reduce.call(toBytes(d), (p, c) => `${ p }${ c.toString(16).padStart(2, '0') }`, '');
};
const fromHex = useBuffer ? hex => {
if (hex instanceof Uint8Array) {
return hex;
}
return globalThis.Buffer.from(hex, 'hex');
} : hex => {
if (hex instanceof Uint8Array) {
return hex;
}
if (!hex.length) {
return new Uint8Array(0);
}
return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
};
function toBytes(obj) {
if (obj instanceof Uint8Array && obj.constructor.name === 'Uint8Array') {
return obj;
}
if (obj instanceof ArrayBuffer) {
return new Uint8Array(obj);
}
if (ArrayBuffer.isView(obj)) {
return new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength);
}
throw new Error('Unknown type, must be binary type');
}
function compare(b1, b2) {
if (isBuffer(b1) && isBuffer(b2)) {
return b1.compare(b2);
}
for (let i = 0; i < b1.length; i++) {
if (b1[i] === b2[i]) {
continue;
}
return b1[i] < b2[i] ? -1 : 1;
}
return 0;
}
function utf8ToBytes(string, units = Infinity) {
let codePoint;
const length = string.length;
let leadSurrogate = null;
const bytes = [];
for (let i = 0; i < length; ++i) {
codePoint = string.charCodeAt(i);
if (codePoint > 55295 && codePoint < 57344) {
if (!leadSurrogate) {
if (codePoint > 56319) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
continue;
} else if (i + 1 === length) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
continue;
}
leadSurrogate = codePoint;
continue;
}
if (codePoint < 56320) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
leadSurrogate = codePoint;
continue;
}
codePoint = (leadSurrogate - 55296 << 10 | codePoint - 56320) + 65536;
} else if (leadSurrogate) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
}
leadSurrogate = null;
if (codePoint < 128) {
if ((units -= 1) < 0)
break;
bytes.push(codePoint);
} else if (codePoint < 2048) {
if ((units -= 2) < 0)
break;
bytes.push(codePoint >> 6 | 192, codePoint & 63 | 128);
} else if (codePoint < 65536) {
if ((units -= 3) < 0)
break;
bytes.push(codePoint >> 12 | 224, codePoint >> 6 & 63 | 128, codePoint & 63 | 128);
} else if (codePoint < 1114112) {
if ((units -= 4) < 0)
break;
bytes.push(codePoint >> 18 | 240, codePoint >> 12 & 63 | 128, codePoint >> 6 & 63 | 128, codePoint & 63 | 128);
} else {
throw new Error('Invalid code point');
}
}
return bytes;
}
function utf8Slice(buf, offset, end) {
const res = [];
while (offset < end) {
const firstByte = buf[offset];
let codePoint = null;
let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1;
if (offset + bytesPerSequence <= end) {
let secondByte, thirdByte, fourthByte, tempCodePoint;
switch (bytesPerSequence) {
case 1:
if (firstByte < 128) {
codePoint = firstByte;
}
break;
case 2:
secondByte = buf[offset + 1];
if ((secondByte & 192) === 128) {
tempCodePoint = (firstByte & 31) << 6 | secondByte & 63;
if (tempCodePoint > 127) {
codePoint = tempCodePoint;
}
}
break;
case 3:
secondByte = buf[offset + 1];
thirdByte = buf[offset + 2];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63;
if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) {
codePoint = tempCodePoint;
}
}
break;
case 4:
secondByte = buf[offset + 1];
thirdByte = buf[offset + 2];
fourthByte = buf[offset + 3];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63;
if (tempCodePoint > 65535 && tempCodePoint < 1114112) {
codePoint = tempCodePoint;
}
}
}
}
if (codePoint === null) {
codePoint = 65533;
bytesPerSequence = 1;
} else if (codePoint > 65535) {
codePoint -= 65536;
res.push(codePoint >>> 10 & 1023 | 55296);
codePoint = 56320 | codePoint & 1023;
}
res.push(codePoint);
offset += bytesPerSequence;
}
return decodeCodePointsArray(res);
}
const MAX_ARGUMENTS_LENGTH = 4096;
function decodeCodePointsArray(codePoints) {
const len = codePoints.length;
if (len <= MAX_ARGUMENTS_LENGTH) {
return String.fromCharCode.apply(String, codePoints);
}
let res = '';
let i = 0;
while (i < len) {
res += String.fromCharCode.apply(String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH));
}
return res;
}
exports.alloc = alloc;
exports.asU8A = asU8A;
exports.compare = compare;
exports.concat = concat;
exports.decodeCodePointsArray = decodeCodePointsArray;
exports.fromArray = fromArray;
exports.fromHex = fromHex;
exports.fromString = fromString;
exports.slice = slice;
exports.toHex = toHex;
exports.toString = toString;
exports.useBuffer = useBuffer;
+22
View File
@@ -0,0 +1,22 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const decodeErrPrefix = 'CBOR decode error:';
const encodeErrPrefix = 'CBOR encode error:';
const uintMinorPrefixBytes = [];
uintMinorPrefixBytes[23] = 1;
uintMinorPrefixBytes[24] = 2;
uintMinorPrefixBytes[25] = 3;
uintMinorPrefixBytes[26] = 5;
uintMinorPrefixBytes[27] = 9;
function assertEnoughData(data, pos, need) {
if (data.length - pos < need) {
throw new Error(`${ decodeErrPrefix } not enough data for type`);
}
}
exports.assertEnoughData = assertEnoughData;
exports.decodeErrPrefix = decodeErrPrefix;
exports.encodeErrPrefix = encodeErrPrefix;
exports.uintMinorPrefixBytes = uintMinorPrefixBytes;
+140
View File
@@ -0,0 +1,140 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var common = require('./common.js');
var token = require('./token.js');
var jump = require('./jump.js');
const defaultDecodeOptions = {
strict: false,
allowIndefinite: true,
allowUndefined: true,
allowBigInt: true
};
class Tokeniser {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
}
done() {
return this.pos >= this.data.length;
}
next() {
const byt = this.data[this.pos];
let token = jump.quick[byt];
if (token === undefined) {
const decoder = jump.jump[byt];
if (!decoder) {
throw new Error(`${ common.decodeErrPrefix } no decoder for major type ${ byt >>> 5 } (byte 0x${ byt.toString(16).padStart(2, '0') })`);
}
const minor = byt & 31;
token = decoder(this.data, this.pos, minor, this.options);
}
this.pos += token.encodedLength;
return token;
}
}
const DONE = Symbol.for('DONE');
const BREAK = Symbol.for('BREAK');
function tokenToArray(token, tokeniser, options) {
const arr = [];
for (let i = 0; i < token.value; i++) {
const value = tokensToObject(tokeniser, options);
if (value === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ common.decodeErrPrefix } got unexpected break to lengthed array`);
}
if (value === DONE) {
throw new Error(`${ common.decodeErrPrefix } found array but not enough entries (got ${ i }, expected ${ token.value })`);
}
arr[i] = value;
}
return arr;
}
function tokenToMap(token, tokeniser, options) {
const useMaps = options.useMaps === true;
const obj = useMaps ? undefined : {};
const m = useMaps ? new Map() : undefined;
for (let i = 0; i < token.value; i++) {
const key = tokensToObject(tokeniser, options);
if (key === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ common.decodeErrPrefix } got unexpected break to lengthed map`);
}
if (key === DONE) {
throw new Error(`${ common.decodeErrPrefix } found map but not enough entries (got ${ i } [no key], expected ${ token.value })`);
}
if (useMaps !== true && typeof key !== 'string') {
throw new Error(`${ common.decodeErrPrefix } non-string keys not supported (got ${ typeof key })`);
}
if (options.rejectDuplicateMapKeys === true) {
if (useMaps && m.has(key) || !useMaps && key in obj) {
throw new Error(`${ common.decodeErrPrefix } found repeat map key "${ key }"`);
}
}
const value = tokensToObject(tokeniser, options);
if (value === DONE) {
throw new Error(`${ common.decodeErrPrefix } found map but not enough entries (got ${ i } [no value], expected ${ token.value })`);
}
if (useMaps) {
m.set(key, value);
} else {
obj[key] = value;
}
}
return useMaps ? m : obj;
}
function tokensToObject(tokeniser, options) {
if (tokeniser.done()) {
return DONE;
}
const token$1 = tokeniser.next();
if (token$1.type === token.Type.break) {
return BREAK;
}
if (token$1.type.terminal) {
return token$1.value;
}
if (token$1.type === token.Type.array) {
return tokenToArray(token$1, tokeniser, options);
}
if (token$1.type === token.Type.map) {
return tokenToMap(token$1, tokeniser, options);
}
if (token$1.type === token.Type.tag) {
if (options.tags && typeof options.tags[token$1.value] === 'function') {
const tagged = tokensToObject(tokeniser, options);
return options.tags[token$1.value](tagged);
}
throw new Error(`${ common.decodeErrPrefix } tag not supported (${ token$1.value })`);
}
throw new Error('unsupported');
}
function decode(data, options) {
if (!(data instanceof Uint8Array)) {
throw new Error(`${ common.decodeErrPrefix } data to decode must be a Uint8Array`);
}
options = Object.assign({}, defaultDecodeOptions, options);
const tokeniser = options.tokenizer || new Tokeniser(data, options);
const decoded = tokensToObject(tokeniser, options);
if (decoded === DONE) {
throw new Error(`${ common.decodeErrPrefix } did not find any content to decode`);
}
if (decoded === BREAK) {
throw new Error(`${ common.decodeErrPrefix } got unexpected break`);
}
if (!tokeniser.done()) {
throw new Error(`${ common.decodeErrPrefix } too many terminals, data makes no sense`);
}
return decoded;
}
exports.Tokeniser = Tokeniser;
exports.decode = decode;
exports.tokensToObject = tokensToObject;
+124
View File
@@ -0,0 +1,124 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var decode = require('./decode.js');
var byteUtils = require('./byte-utils.js');
var _0uint = require('./0uint.js');
const utf8Encoder = new TextEncoder();
const utf8Decoder = new TextDecoder();
function* tokensToDiagnostic(inp, width = 100) {
const tokeniser = new decode.Tokeniser(inp, {
retainStringBytes: true,
allowBigInt: true
});
let pos = 0;
const indent = [];
const slc = (start, length) => {
return byteUtils.toHex(inp.slice(pos + start, pos + start + length));
};
while (!tokeniser.done()) {
const token = tokeniser.next();
let margin = ''.padStart(indent.length * 2, ' ');
let vLength = token.encodedLength - 1;
let v = String(token.value);
let outp = `${ margin }${ slc(0, 1) }`;
const str = token.type.name === 'bytes' || token.type.name === 'string';
if (token.type.name === 'string') {
v = v.length;
vLength -= v;
} else if (token.type.name === 'bytes') {
v = token.value.length;
vLength -= v;
}
let multilen;
switch (token.type.name) {
case 'string':
case 'bytes':
case 'map':
case 'array':
multilen = token.type.name === 'string' ? utf8Encoder.encode(token.value).length : token.value.length;
if (multilen >= _0uint.uintBoundaries[0]) {
if (multilen < _0uint.uintBoundaries[1]) {
outp += ` ${ slc(1, 1) }`;
} else if (multilen < _0uint.uintBoundaries[2]) {
outp += ` ${ slc(1, 2) }`;
} else if (multilen < _0uint.uintBoundaries[3]) {
outp += ` ${ slc(1, 4) }`;
} else if (multilen < _0uint.uintBoundaries[4]) {
outp += ` ${ slc(1, 8) }`;
}
}
break;
default:
outp += ` ${ slc(1, vLength) }`;
break;
}
outp = outp.padEnd(width / 2, ' ');
outp += `# ${ margin }${ token.type.name }`;
if (token.type.name !== v) {
outp += `(${ v })`;
}
yield outp;
if (str) {
let asString = token.type.name === 'string';
margin += ' ';
let repr = asString ? utf8Encoder.encode(token.value) : token.value;
if (asString && token.byteValue !== undefined) {
if (repr.length !== token.byteValue.length) {
repr = token.byteValue;
asString = false;
}
}
const wh = (width / 2 - margin.length - 1) / 2;
let snip = 0;
while (repr.length - snip > 0) {
const piece = repr.slice(snip, snip + wh);
snip += piece.length;
const st = asString ? utf8Decoder.decode(piece) : piece.reduce((p, c) => {
if (c < 32 || c >= 127 && c < 161 || c === 173) {
return `${ p }\\x${ c.toString(16).padStart(2, '0') }`;
}
return `${ p }${ String.fromCharCode(c) }`;
}, '');
yield `${ margin }${ byteUtils.toHex(piece) }`.padEnd(width / 2, ' ') + `# ${ margin }"${ st }"`;
}
}
if (indent.length) {
indent[indent.length - 1]--;
}
if (!token.type.terminal) {
switch (token.type.name) {
case 'map':
indent.push(token.value * 2);
break;
case 'array':
indent.push(token.value);
break;
case 'tag':
indent.push(1);
break;
default:
throw new Error(`Unknown token type '${ token.type.name }'`);
}
}
while (indent.length && indent[indent.length - 1] <= 0) {
indent.pop();
}
pos += token.encodedLength;
}
}
function fromDiag(input) {
if (typeof input !== 'string') {
throw new TypeError('Expected string input');
}
input = input.replace(/#.*?$/mg, '').replace(/[\s\r\n]+/mg, '');
if (/[^a-f0-9]/i.test(input)) {
throw new TypeError('Input string was not CBOR diagnostic format');
}
return byteUtils.fromHex(input);
}
exports.fromDiag = fromDiag;
exports.tokensToDiagnostic = tokensToDiagnostic;
+248
View File
@@ -0,0 +1,248 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var is = require('./is.js');
var token = require('./token.js');
var bl = require('./bl.js');
var common = require('./common.js');
var jump = require('./jump.js');
var byteUtils = require('./byte-utils.js');
var _0uint = require('./0uint.js');
var _1negint = require('./1negint.js');
var _2bytes = require('./2bytes.js');
var _3string = require('./3string.js');
var _4array = require('./4array.js');
var _5map = require('./5map.js');
var _6tag = require('./6tag.js');
var _7float = require('./7float.js');
const defaultEncodeOptions = {
float64: false,
mapSorter,
quickEncodeToken: jump.quickEncodeToken
};
function makeCborEncoders() {
const encoders = [];
encoders[token.Type.uint.major] = _0uint.encodeUint;
encoders[token.Type.negint.major] = _1negint.encodeNegint;
encoders[token.Type.bytes.major] = _2bytes.encodeBytes;
encoders[token.Type.string.major] = _3string.encodeString;
encoders[token.Type.array.major] = _4array.encodeArray;
encoders[token.Type.map.major] = _5map.encodeMap;
encoders[token.Type.tag.major] = _6tag.encodeTag;
encoders[token.Type.float.major] = _7float.encodeFloat;
return encoders;
}
const cborEncoders = makeCborEncoders();
const buf = new bl.Bl();
class Ref {
constructor(obj, parent) {
this.obj = obj;
this.parent = parent;
}
includes(obj) {
let p = this;
do {
if (p.obj === obj) {
return true;
}
} while (p = p.parent);
return false;
}
static createCheck(stack, obj) {
if (stack && stack.includes(obj)) {
throw new Error(`${ common.encodeErrPrefix } object contains circular references`);
}
return new Ref(obj, stack);
}
}
const simpleTokens = {
null: new token.Token(token.Type.null, null),
undefined: new token.Token(token.Type.undefined, undefined),
true: new token.Token(token.Type.true, true),
false: new token.Token(token.Type.false, false),
emptyArray: new token.Token(token.Type.array, 0),
emptyMap: new token.Token(token.Type.map, 0)
};
const typeEncoders = {
number(obj, _typ, _options, _refStack) {
if (!Number.isInteger(obj) || !Number.isSafeInteger(obj)) {
return new token.Token(token.Type.float, obj);
} else if (obj >= 0) {
return new token.Token(token.Type.uint, obj);
} else {
return new token.Token(token.Type.negint, obj);
}
},
bigint(obj, _typ, _options, _refStack) {
if (obj >= BigInt(0)) {
return new token.Token(token.Type.uint, obj);
} else {
return new token.Token(token.Type.negint, obj);
}
},
Uint8Array(obj, _typ, _options, _refStack) {
return new token.Token(token.Type.bytes, obj);
},
string(obj, _typ, _options, _refStack) {
return new token.Token(token.Type.string, obj);
},
boolean(obj, _typ, _options, _refStack) {
return obj ? simpleTokens.true : simpleTokens.false;
},
null(_obj, _typ, _options, _refStack) {
return simpleTokens.null;
},
undefined(_obj, _typ, _options, _refStack) {
return simpleTokens.undefined;
},
ArrayBuffer(obj, _typ, _options, _refStack) {
return new token.Token(token.Type.bytes, new Uint8Array(obj));
},
DataView(obj, _typ, _options, _refStack) {
return new token.Token(token.Type.bytes, new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength));
},
Array(obj, _typ, options, refStack) {
if (!obj.length) {
if (options.addBreakTokens === true) {
return [
simpleTokens.emptyArray,
new token.Token(token.Type.break)
];
}
return simpleTokens.emptyArray;
}
refStack = Ref.createCheck(refStack, obj);
const entries = [];
let i = 0;
for (const e of obj) {
entries[i++] = objectToTokens(e, options, refStack);
}
if (options.addBreakTokens) {
return [
new token.Token(token.Type.array, obj.length),
entries,
new token.Token(token.Type.break)
];
}
return [
new token.Token(token.Type.array, obj.length),
entries
];
},
Object(obj, typ, options, refStack) {
const isMap = typ !== 'Object';
const keys = isMap ? obj.keys() : Object.keys(obj);
const length = isMap ? obj.size : keys.length;
if (!length) {
if (options.addBreakTokens === true) {
return [
simpleTokens.emptyMap,
new token.Token(token.Type.break)
];
}
return simpleTokens.emptyMap;
}
refStack = Ref.createCheck(refStack, obj);
const entries = [];
let i = 0;
for (const key of keys) {
entries[i++] = [
objectToTokens(key, options, refStack),
objectToTokens(isMap ? obj.get(key) : obj[key], options, refStack)
];
}
sortMapEntries(entries, options);
if (options.addBreakTokens) {
return [
new token.Token(token.Type.map, length),
entries,
new token.Token(token.Type.break)
];
}
return [
new token.Token(token.Type.map, length),
entries
];
}
};
typeEncoders.Map = typeEncoders.Object;
typeEncoders.Buffer = typeEncoders.Uint8Array;
for (const typ of 'Uint8Clamped Uint16 Uint32 Int8 Int16 Int32 BigUint64 BigInt64 Float32 Float64'.split(' ')) {
typeEncoders[`${ typ }Array`] = typeEncoders.DataView;
}
function objectToTokens(obj, options = {}, refStack) {
const typ = is.is(obj);
const customTypeEncoder = options && options.typeEncoders && options.typeEncoders[typ] || typeEncoders[typ];
if (typeof customTypeEncoder === 'function') {
const tokens = customTypeEncoder(obj, typ, options, refStack);
if (tokens != null) {
return tokens;
}
}
const typeEncoder = typeEncoders[typ];
if (!typeEncoder) {
throw new Error(`${ common.encodeErrPrefix } unsupported type: ${ typ }`);
}
return typeEncoder(obj, typ, options, refStack);
}
function sortMapEntries(entries, options) {
if (options.mapSorter) {
entries.sort(options.mapSorter);
}
}
function mapSorter(e1, e2) {
const keyToken1 = Array.isArray(e1[0]) ? e1[0][0] : e1[0];
const keyToken2 = Array.isArray(e2[0]) ? e2[0][0] : e2[0];
if (keyToken1.type !== keyToken2.type) {
return keyToken1.type.compare(keyToken2.type);
}
const major = keyToken1.type.major;
const tcmp = cborEncoders[major].compareTokens(keyToken1, keyToken2);
if (tcmp === 0) {
console.warn('WARNING: complex key types used, CBOR key sorting guarantees are gone');
}
return tcmp;
}
function tokensToEncoded(buf, tokens, encoders, options) {
if (Array.isArray(tokens)) {
for (const token of tokens) {
tokensToEncoded(buf, token, encoders, options);
}
} else {
encoders[tokens.type.major](buf, tokens, options);
}
}
function encodeCustom(data, encoders, options) {
const tokens = objectToTokens(data, options);
if (!Array.isArray(tokens) && options.quickEncodeToken) {
const quickBytes = options.quickEncodeToken(tokens);
if (quickBytes) {
return quickBytes;
}
const encoder = encoders[tokens.type.major];
if (encoder.encodedSize) {
const size = encoder.encodedSize(tokens, options);
const buf = new bl.Bl(size);
encoder(buf, tokens, options);
if (buf.chunks.length !== 1) {
throw new Error(`Unexpected error: pre-calculated length for ${ tokens } was wrong`);
}
return byteUtils.asU8A(buf.chunks[0]);
}
}
buf.reset();
tokensToEncoded(buf, tokens, encoders, options);
return buf.toBytes(true);
}
function encode(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
return encodeCustom(data, cborEncoders, options);
}
exports.Ref = Ref;
exports.encode = encode;
exports.encodeCustom = encodeCustom;
exports.makeCborEncoders = makeCborEncoders;
exports.objectToTokens = objectToTokens;
+87
View File
@@ -0,0 +1,87 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const typeofs = [
'string',
'number',
'bigint',
'symbol'
];
const objectTypeNames = [
'Function',
'Generator',
'AsyncGenerator',
'GeneratorFunction',
'AsyncGeneratorFunction',
'AsyncFunction',
'Observable',
'Array',
'Buffer',
'Object',
'RegExp',
'Date',
'Error',
'Map',
'Set',
'WeakMap',
'WeakSet',
'ArrayBuffer',
'SharedArrayBuffer',
'DataView',
'Promise',
'URL',
'HTMLElement',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'BigInt64Array',
'BigUint64Array'
];
function is(value) {
if (value === null) {
return 'null';
}
if (value === undefined) {
return 'undefined';
}
if (value === true || value === false) {
return 'boolean';
}
const typeOf = typeof value;
if (typeofs.includes(typeOf)) {
return typeOf;
}
if (typeOf === 'function') {
return 'Function';
}
if (Array.isArray(value)) {
return 'Array';
}
if (isBuffer(value)) {
return 'Buffer';
}
const objectType = getObjectType(value);
if (objectType) {
return objectType;
}
return 'Object';
}
function isBuffer(value) {
return value && value.constructor && value.constructor.isBuffer && value.constructor.isBuffer.call(null, value);
}
function getObjectType(value) {
const objectTypeName = Object.prototype.toString.call(value).slice(8, -1);
if (objectTypeNames.includes(objectTypeName)) {
return objectTypeName;
}
return undefined;
}
exports.is = is;
+414
View File
@@ -0,0 +1,414 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var decode$1 = require('../decode.js');
var token = require('../token.js');
var byteUtils = require('../byte-utils.js');
var common = require('../common.js');
class Tokenizer {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
this.modeStack = ['value'];
this.lastToken = '';
}
done() {
return this.pos >= this.data.length;
}
ch() {
return this.data[this.pos];
}
currentMode() {
return this.modeStack[this.modeStack.length - 1];
}
skipWhitespace() {
let c = this.ch();
while (c === 32 || c === 9 || c === 13 || c === 10) {
c = this.data[++this.pos];
}
}
expect(str) {
if (this.data.length - this.pos < str.length) {
throw new Error(`${ common.decodeErrPrefix } unexpected end of input at position ${ this.pos }`);
}
for (let i = 0; i < str.length; i++) {
if (this.data[this.pos++] !== str[i]) {
throw new Error(`${ common.decodeErrPrefix } unexpected token at position ${ this.pos }, expected to find '${ String.fromCharCode(...str) }'`);
}
}
}
parseNumber() {
const startPos = this.pos;
let negative = false;
let float = false;
const swallow = chars => {
while (!this.done()) {
const ch = this.ch();
if (chars.includes(ch)) {
this.pos++;
} else {
break;
}
}
};
if (this.ch() === 45) {
negative = true;
this.pos++;
}
if (this.ch() === 48) {
this.pos++;
if (this.ch() === 46) {
this.pos++;
float = true;
} else {
return new token.Token(token.Type.uint, 0, this.pos - startPos);
}
}
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
if (negative && this.pos === startPos + 1) {
throw new Error(`${ common.decodeErrPrefix } unexpected token at position ${ this.pos }`);
}
if (!this.done() && this.ch() === 46) {
if (float) {
throw new Error(`${ common.decodeErrPrefix } unexpected token at position ${ this.pos }`);
}
float = true;
this.pos++;
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
}
if (!this.done() && (this.ch() === 101 || this.ch() === 69)) {
float = true;
this.pos++;
if (!this.done() && (this.ch() === 43 || this.ch() === 45)) {
this.pos++;
}
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
}
const numStr = String.fromCharCode.apply(null, this.data.subarray(startPos, this.pos));
const num = parseFloat(numStr);
if (float) {
return new token.Token(token.Type.float, num, this.pos - startPos);
}
if (this.options.allowBigInt !== true || Number.isSafeInteger(num)) {
return new token.Token(num >= 0 ? token.Type.uint : token.Type.negint, num, this.pos - startPos);
}
return new token.Token(num >= 0 ? token.Type.uint : token.Type.negint, BigInt(numStr), this.pos - startPos);
}
parseString() {
if (this.ch() !== 34) {
throw new Error(`${ common.decodeErrPrefix } unexpected character at position ${ this.pos }; this shouldn't happen`);
}
this.pos++;
for (let i = this.pos, l = 0; i < this.data.length && l < 65536; i++, l++) {
const ch = this.data[i];
if (ch === 92 || ch < 32 || ch >= 128) {
break;
}
if (ch === 34) {
const str = String.fromCharCode.apply(null, this.data.subarray(this.pos, i));
this.pos = i + 1;
return new token.Token(token.Type.string, str, l);
}
}
const startPos = this.pos;
const chars = [];
const readu4 = () => {
if (this.pos + 4 >= this.data.length) {
throw new Error(`${ common.decodeErrPrefix } unexpected end of unicode escape sequence at position ${ this.pos }`);
}
let u4 = 0;
for (let i = 0; i < 4; i++) {
let ch = this.ch();
if (ch >= 48 && ch <= 57) {
ch -= 48;
} else if (ch >= 97 && ch <= 102) {
ch = ch - 97 + 10;
} else if (ch >= 65 && ch <= 70) {
ch = ch - 65 + 10;
} else {
throw new Error(`${ common.decodeErrPrefix } unexpected unicode escape character at position ${ this.pos }`);
}
u4 = u4 * 16 + ch;
this.pos++;
}
return u4;
};
const readUtf8Char = () => {
const firstByte = this.ch();
let codePoint = null;
let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1;
if (this.pos + bytesPerSequence > this.data.length) {
throw new Error(`${ common.decodeErrPrefix } unexpected unicode sequence at position ${ this.pos }`);
}
let secondByte, thirdByte, fourthByte, tempCodePoint;
switch (bytesPerSequence) {
case 1:
if (firstByte < 128) {
codePoint = firstByte;
}
break;
case 2:
secondByte = this.data[this.pos + 1];
if ((secondByte & 192) === 128) {
tempCodePoint = (firstByte & 31) << 6 | secondByte & 63;
if (tempCodePoint > 127) {
codePoint = tempCodePoint;
}
}
break;
case 3:
secondByte = this.data[this.pos + 1];
thirdByte = this.data[this.pos + 2];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63;
if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) {
codePoint = tempCodePoint;
}
}
break;
case 4:
secondByte = this.data[this.pos + 1];
thirdByte = this.data[this.pos + 2];
fourthByte = this.data[this.pos + 3];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63;
if (tempCodePoint > 65535 && tempCodePoint < 1114112) {
codePoint = tempCodePoint;
}
}
}
if (codePoint === null) {
codePoint = 65533;
bytesPerSequence = 1;
} else if (codePoint > 65535) {
codePoint -= 65536;
chars.push(codePoint >>> 10 & 1023 | 55296);
codePoint = 56320 | codePoint & 1023;
}
chars.push(codePoint);
this.pos += bytesPerSequence;
};
while (!this.done()) {
const ch = this.ch();
let ch1;
switch (ch) {
case 92:
this.pos++;
if (this.done()) {
throw new Error(`${ common.decodeErrPrefix } unexpected string termination at position ${ this.pos }`);
}
ch1 = this.ch();
this.pos++;
switch (ch1) {
case 34:
case 39:
case 92:
case 47:
chars.push(ch1);
break;
case 98:
chars.push(8);
break;
case 116:
chars.push(9);
break;
case 110:
chars.push(10);
break;
case 102:
chars.push(12);
break;
case 114:
chars.push(13);
break;
case 117:
chars.push(readu4());
break;
default:
throw new Error(`${ common.decodeErrPrefix } unexpected string escape character at position ${ this.pos }`);
}
break;
case 34:
this.pos++;
return new token.Token(token.Type.string, byteUtils.decodeCodePointsArray(chars), this.pos - startPos);
default:
if (ch < 32) {
throw new Error(`${ common.decodeErrPrefix } invalid control character at position ${ this.pos }`);
} else if (ch < 128) {
chars.push(ch);
this.pos++;
} else {
readUtf8Char();
}
}
}
throw new Error(`${ common.decodeErrPrefix } unexpected end of string at position ${ this.pos }`);
}
parseValue() {
switch (this.ch()) {
case 123:
this.modeStack.push('obj-start');
this.pos++;
return new token.Token(token.Type.map, Infinity, 1);
case 91:
this.modeStack.push('array-start');
this.pos++;
return new token.Token(token.Type.array, Infinity, 1);
case 34: {
return this.parseString();
}
case 110:
this.expect([
110,
117,
108,
108
]);
return new token.Token(token.Type.null, null, 4);
case 102:
this.expect([
102,
97,
108,
115,
101
]);
return new token.Token(token.Type.false, false, 5);
case 116:
this.expect([
116,
114,
117,
101
]);
return new token.Token(token.Type.true, true, 4);
case 45:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
return this.parseNumber();
default:
throw new Error(`${ common.decodeErrPrefix } unexpected character at position ${ this.pos }`);
}
}
next() {
this.skipWhitespace();
switch (this.currentMode()) {
case 'value':
this.modeStack.pop();
return this.parseValue();
case 'array-value': {
this.modeStack.pop();
if (this.ch() === 93) {
this.pos++;
this.skipWhitespace();
return new token.Token(token.Type.break, undefined, 1);
}
if (this.ch() !== 44) {
throw new Error(`${ common.decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting array delimiter but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.modeStack.push('array-value');
this.skipWhitespace();
return this.parseValue();
}
case 'array-start': {
this.modeStack.pop();
if (this.ch() === 93) {
this.pos++;
this.skipWhitespace();
return new token.Token(token.Type.break, undefined, 1);
}
this.modeStack.push('array-value');
this.skipWhitespace();
return this.parseValue();
}
case 'obj-key':
if (this.ch() === 125) {
this.modeStack.pop();
this.pos++;
this.skipWhitespace();
return new token.Token(token.Type.break, undefined, 1);
}
if (this.ch() !== 44) {
throw new Error(`${ common.decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting object delimiter but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.skipWhitespace();
case 'obj-start': {
this.modeStack.pop();
if (this.ch() === 125) {
this.pos++;
this.skipWhitespace();
return new token.Token(token.Type.break, undefined, 1);
}
const token$1 = this.parseString();
this.skipWhitespace();
if (this.ch() !== 58) {
throw new Error(`${ common.decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting key/value delimiter ':' but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.modeStack.push('obj-value');
return token$1;
}
case 'obj-value': {
this.modeStack.pop();
this.modeStack.push('obj-key');
this.skipWhitespace();
return this.parseValue();
}
default:
throw new Error(`${ common.decodeErrPrefix } unexpected parse state at position ${ this.pos }; this shouldn't happen`);
}
}
}
function decode(data, options) {
options = Object.assign({ tokenizer: new Tokenizer(data, options) }, options);
return decode$1.decode(data, options);
}
exports.Tokenizer = Tokenizer;
exports.decode = decode;
+161
View File
@@ -0,0 +1,161 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('../token.js');
var encode$1 = require('../encode.js');
var common = require('../common.js');
var byteUtils = require('../byte-utils.js');
class JSONEncoder extends Array {
constructor() {
super();
this.inRecursive = [];
}
prefix(buf) {
const recurs = this.inRecursive[this.inRecursive.length - 1];
if (recurs) {
if (recurs.type === token.Type.array) {
recurs.elements++;
if (recurs.elements !== 1) {
buf.push([44]);
}
}
if (recurs.type === token.Type.map) {
recurs.elements++;
if (recurs.elements !== 1) {
if (recurs.elements % 2 === 1) {
buf.push([44]);
} else {
buf.push([58]);
}
}
}
}
}
[token.Type.uint.major](buf, token) {
this.prefix(buf);
const is = String(token.value);
const isa = [];
for (let i = 0; i < is.length; i++) {
isa[i] = is.charCodeAt(i);
}
buf.push(isa);
}
[token.Type.negint.major](buf, token$1) {
this[token.Type.uint.major](buf, token$1);
}
[token.Type.bytes.major](_buf, _token) {
throw new Error(`${ common.encodeErrPrefix } unsupported type: Uint8Array`);
}
[token.Type.string.major](buf, token) {
this.prefix(buf);
const byts = byteUtils.fromString(JSON.stringify(token.value));
buf.push(byts.length > 32 ? byteUtils.asU8A(byts) : byts);
}
[token.Type.array.major](buf, _token) {
this.prefix(buf);
this.inRecursive.push({
type: token.Type.array,
elements: 0
});
buf.push([91]);
}
[token.Type.map.major](buf, _token) {
this.prefix(buf);
this.inRecursive.push({
type: token.Type.map,
elements: 0
});
buf.push([123]);
}
[token.Type.tag.major](_buf, _token) {
}
[token.Type.float.major](buf, token$1) {
if (token$1.type.name === 'break') {
const recurs = this.inRecursive.pop();
if (recurs) {
if (recurs.type === token.Type.array) {
buf.push([93]);
} else if (recurs.type === token.Type.map) {
buf.push([125]);
} else {
throw new Error('Unexpected recursive type; this should not happen!');
}
return;
}
throw new Error('Unexpected break; this should not happen!');
}
if (token$1.value === undefined) {
throw new Error(`${ common.encodeErrPrefix } unsupported type: undefined`);
}
this.prefix(buf);
if (token$1.type.name === 'true') {
buf.push([
116,
114,
117,
101
]);
return;
} else if (token$1.type.name === 'false') {
buf.push([
102,
97,
108,
115,
101
]);
return;
} else if (token$1.type.name === 'null') {
buf.push([
110,
117,
108,
108
]);
return;
}
const is = String(token$1.value);
const isa = [];
let dp = false;
for (let i = 0; i < is.length; i++) {
isa[i] = is.charCodeAt(i);
if (!dp && (isa[i] === 46 || isa[i] === 101 || isa[i] === 69)) {
dp = true;
}
}
if (!dp) {
isa.push(46);
isa.push(48);
}
buf.push(isa);
}
}
function mapSorter(e1, e2) {
if (Array.isArray(e1[0]) || Array.isArray(e2[0])) {
throw new Error(`${ common.encodeErrPrefix } complex map keys are not supported`);
}
const keyToken1 = e1[0];
const keyToken2 = e2[0];
if (keyToken1.type !== token.Type.string || keyToken2.type !== token.Type.string) {
throw new Error(`${ common.encodeErrPrefix } non-string map keys are not supported`);
}
if (keyToken1 < keyToken2) {
return -1;
}
if (keyToken1 > keyToken2) {
return 1;
}
throw new Error(`${ common.encodeErrPrefix } unexpected duplicate map keys, this is not supported`);
}
const defaultEncodeOptions = {
addBreakTokens: true,
mapSorter
};
function encode(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
return encode$1.encodeCustom(data, new JSONEncoder(), options);
}
exports.encode = encode;
+12
View File
@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var encode = require('./encode.js');
var decode = require('./decode.js');
exports.encode = encode.encode;
exports.Tokenizer = decode.Tokenizer;
exports.decode = decode.decode;
+174
View File
@@ -0,0 +1,174 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('./token.js');
var _0uint = require('./0uint.js');
var _1negint = require('./1negint.js');
var _2bytes = require('./2bytes.js');
var _3string = require('./3string.js');
var _4array = require('./4array.js');
var _5map = require('./5map.js');
var _6tag = require('./6tag.js');
var _7float = require('./7float.js');
var common = require('./common.js');
var byteUtils = require('./byte-utils.js');
function invalidMinor(data, pos, minor) {
throw new Error(`${ common.decodeErrPrefix } encountered invalid minor (${ minor }) for major ${ data[pos] >>> 5 }`);
}
function errorer(msg) {
return () => {
throw new Error(`${ common.decodeErrPrefix } ${ msg }`);
};
}
const jump = [];
for (let i = 0; i <= 23; i++) {
jump[i] = invalidMinor;
}
jump[24] = _0uint.decodeUint8;
jump[25] = _0uint.decodeUint16;
jump[26] = _0uint.decodeUint32;
jump[27] = _0uint.decodeUint64;
jump[28] = invalidMinor;
jump[29] = invalidMinor;
jump[30] = invalidMinor;
jump[31] = invalidMinor;
for (let i = 32; i <= 55; i++) {
jump[i] = invalidMinor;
}
jump[56] = _1negint.decodeNegint8;
jump[57] = _1negint.decodeNegint16;
jump[58] = _1negint.decodeNegint32;
jump[59] = _1negint.decodeNegint64;
jump[60] = invalidMinor;
jump[61] = invalidMinor;
jump[62] = invalidMinor;
jump[63] = invalidMinor;
for (let i = 64; i <= 87; i++) {
jump[i] = _2bytes.decodeBytesCompact;
}
jump[88] = _2bytes.decodeBytes8;
jump[89] = _2bytes.decodeBytes16;
jump[90] = _2bytes.decodeBytes32;
jump[91] = _2bytes.decodeBytes64;
jump[92] = invalidMinor;
jump[93] = invalidMinor;
jump[94] = invalidMinor;
jump[95] = errorer('indefinite length bytes/strings are not supported');
for (let i = 96; i <= 119; i++) {
jump[i] = _3string.decodeStringCompact;
}
jump[120] = _3string.decodeString8;
jump[121] = _3string.decodeString16;
jump[122] = _3string.decodeString32;
jump[123] = _3string.decodeString64;
jump[124] = invalidMinor;
jump[125] = invalidMinor;
jump[126] = invalidMinor;
jump[127] = errorer('indefinite length bytes/strings are not supported');
for (let i = 128; i <= 151; i++) {
jump[i] = _4array.decodeArrayCompact;
}
jump[152] = _4array.decodeArray8;
jump[153] = _4array.decodeArray16;
jump[154] = _4array.decodeArray32;
jump[155] = _4array.decodeArray64;
jump[156] = invalidMinor;
jump[157] = invalidMinor;
jump[158] = invalidMinor;
jump[159] = _4array.decodeArrayIndefinite;
for (let i = 160; i <= 183; i++) {
jump[i] = _5map.decodeMapCompact;
}
jump[184] = _5map.decodeMap8;
jump[185] = _5map.decodeMap16;
jump[186] = _5map.decodeMap32;
jump[187] = _5map.decodeMap64;
jump[188] = invalidMinor;
jump[189] = invalidMinor;
jump[190] = invalidMinor;
jump[191] = _5map.decodeMapIndefinite;
for (let i = 192; i <= 215; i++) {
jump[i] = _6tag.decodeTagCompact;
}
jump[216] = _6tag.decodeTag8;
jump[217] = _6tag.decodeTag16;
jump[218] = _6tag.decodeTag32;
jump[219] = _6tag.decodeTag64;
jump[220] = invalidMinor;
jump[221] = invalidMinor;
jump[222] = invalidMinor;
jump[223] = invalidMinor;
for (let i = 224; i <= 243; i++) {
jump[i] = errorer('simple values are not supported');
}
jump[244] = invalidMinor;
jump[245] = invalidMinor;
jump[246] = invalidMinor;
jump[247] = _7float.decodeUndefined;
jump[248] = errorer('simple values are not supported');
jump[249] = _7float.decodeFloat16;
jump[250] = _7float.decodeFloat32;
jump[251] = _7float.decodeFloat64;
jump[252] = invalidMinor;
jump[253] = invalidMinor;
jump[254] = invalidMinor;
jump[255] = _7float.decodeBreak;
const quick = [];
for (let i = 0; i < 24; i++) {
quick[i] = new token.Token(token.Type.uint, i, 1);
}
for (let i = -1; i >= -24; i--) {
quick[31 - i] = new token.Token(token.Type.negint, i, 1);
}
quick[64] = new token.Token(token.Type.bytes, new Uint8Array(0), 1);
quick[96] = new token.Token(token.Type.string, '', 1);
quick[128] = new token.Token(token.Type.array, 0, 1);
quick[160] = new token.Token(token.Type.map, 0, 1);
quick[244] = new token.Token(token.Type.false, false, 1);
quick[245] = new token.Token(token.Type.true, true, 1);
quick[246] = new token.Token(token.Type.null, null, 1);
function quickEncodeToken(token$1) {
switch (token$1.type) {
case token.Type.false:
return byteUtils.fromArray([244]);
case token.Type.true:
return byteUtils.fromArray([245]);
case token.Type.null:
return byteUtils.fromArray([246]);
case token.Type.bytes:
if (!token$1.value.length) {
return byteUtils.fromArray([64]);
}
return;
case token.Type.string:
if (token$1.value === '') {
return byteUtils.fromArray([96]);
}
return;
case token.Type.array:
if (token$1.value === 0) {
return byteUtils.fromArray([128]);
}
return;
case token.Type.map:
if (token$1.value === 0) {
return byteUtils.fromArray([160]);
}
return;
case token.Type.uint:
if (token$1.value < 24) {
return byteUtils.fromArray([Number(token$1.value)]);
}
return;
case token.Type.negint:
if (token$1.value >= -24) {
return byteUtils.fromArray([31 - Number(token$1.value)]);
}
}
}
exports.jump = jump;
exports.quick = quick;
exports.quickEncodeToken = quickEncodeToken;
+36
View File
@@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var encode = require('./encode.js');
var jump = require('./jump.js');
const cborEncoders = encode.makeCborEncoders();
const defaultEncodeOptions = {
float64: false,
quickEncodeToken: jump.quickEncodeToken
};
function encodedLength(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
options.mapSorter = undefined;
const tokens = encode.objectToTokens(data, options);
return tokensToLength(tokens, cborEncoders, options);
}
function tokensToLength(tokens, encoders = cborEncoders, options = defaultEncodeOptions) {
if (Array.isArray(tokens)) {
let len = 0;
for (const token of tokens) {
len += tokensToLength(token, encoders, options);
}
return len;
} else {
const encoder = encoders[tokens.type.major];
if (encoder.encodedSize === undefined || typeof encoder.encodedSize !== 'function') {
throw new Error(`Encoder for ${ tokens.type.name } does not have an encodedSize()`);
}
return encoder.encodedSize(tokens, options);
}
}
exports.encodedLength = encodedLength;
exports.tokensToLength = tokensToLength;
+46
View File
@@ -0,0 +1,46 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
class Type {
constructor(major, name, terminal) {
this.major = major;
this.majorEncoded = major << 5;
this.name = name;
this.terminal = terminal;
}
toString() {
return `Type[${ this.major }].${ this.name }`;
}
compare(typ) {
return this.major < typ.major ? -1 : this.major > typ.major ? 1 : 0;
}
}
Type.uint = new Type(0, 'uint', true);
Type.negint = new Type(1, 'negint', true);
Type.bytes = new Type(2, 'bytes', true);
Type.string = new Type(3, 'string', true);
Type.array = new Type(4, 'array', false);
Type.map = new Type(5, 'map', false);
Type.tag = new Type(6, 'tag', false);
Type.float = new Type(7, 'float', true);
Type.false = new Type(7, 'false', true);
Type.true = new Type(7, 'true', true);
Type.null = new Type(7, 'null', true);
Type.undefined = new Type(7, 'undefined', true);
Type.break = new Type(7, 'break', true);
class Token {
constructor(type, value, encodedLength) {
this.type = type;
this.value = value;
this.encodedLength = encodedLength;
this.encodedBytes = undefined;
this.byteValue = undefined;
}
toString() {
return `Token[${ this.type }].${ this.value }`;
}
}
exports.Token = Token;
exports.Type = Type;
+643
View File
@@ -0,0 +1,643 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const fixtures = [
{
cbor: 'AA==',
hex: '00',
roundtrip: true,
decoded: 0
},
{
cbor: 'AQ==',
hex: '01',
roundtrip: true,
decoded: 1
},
{
cbor: 'Cg==',
hex: '0a',
roundtrip: true,
decoded: 10
},
{
cbor: 'Fw==',
hex: '17',
roundtrip: true,
decoded: 23
},
{
cbor: 'GBg=',
hex: '1818',
roundtrip: true,
decoded: 24
},
{
cbor: 'GBk=',
hex: '1819',
roundtrip: true,
decoded: 25
},
{
cbor: 'GGQ=',
hex: '1864',
roundtrip: true,
decoded: 100
},
{
cbor: 'GQPo',
hex: '1903e8',
roundtrip: true,
decoded: 1000
},
{
cbor: 'GgAPQkA=',
hex: '1a000f4240',
roundtrip: true,
decoded: 1000000
},
{
cbor: 'GwAAAOjUpRAA',
hex: '1b000000e8d4a51000',
roundtrip: true,
decoded: 1000000000000
},
{
cbor: 'G///////////',
hex: '1bffffffffffffffff',
roundtrip: true,
decoded: BigInt('18446744073709551615')
},
{
cbor: 'wkkBAAAAAAAAAAA=',
hex: 'c249010000000000000000',
roundtrip: true,
decoded: BigInt('18446744073709551616'),
noTagDecodeError: /tag not supported \(2\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'O///////////',
hex: '3bffffffffffffffff',
roundtrip: true,
decoded: BigInt('-18446744073709551616')
},
{
cbor: 'w0kBAAAAAAAAAAA=',
hex: 'c349010000000000000000',
roundtrip: true,
decoded: BigInt('-18446744073709551617'),
noTagDecodeError: /tag not supported \(3\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'IA==',
hex: '20',
roundtrip: true,
decoded: -1
},
{
cbor: 'KQ==',
hex: '29',
roundtrip: true,
decoded: -10
},
{
cbor: 'OGM=',
hex: '3863',
roundtrip: true,
decoded: -100
},
{
cbor: 'OQPn',
hex: '3903e7',
roundtrip: true,
decoded: -1000
},
{
cbor: '+QAA',
hex: 'f90000',
roundtrip: false,
decoded: 0
},
{
cbor: '+YAA',
hex: 'f98000',
roundtrip: false,
decoded: -0
},
{
cbor: '+TwA',
hex: 'f93c00',
roundtrip: false,
decoded: 1
},
{
cbor: '+z/xmZmZmZma',
hex: 'fb3ff199999999999a',
roundtrip: true,
decoded: 1.1
},
{
cbor: '+T4A',
hex: 'f93e00',
roundtrip: true,
decoded: 1.5
},
{
cbor: '+Xv/',
hex: 'f97bff',
roundtrip: false,
decoded: 65504
},
{
cbor: '+kfDUAA=',
hex: 'fa47c35000',
roundtrip: false,
decoded: 100000
},
{
cbor: '+n9///8=',
hex: 'fa7f7fffff',
roundtrip: true,
decoded: 3.4028234663852886e+38
},
{
cbor: '+3435DyIAHWc',
hex: 'fb7e37e43c8800759c',
roundtrip: true,
decoded: 1e+300
},
{
cbor: '+QAB',
hex: 'f90001',
roundtrip: true,
decoded: 5.960464477539063e-8
},
{
cbor: '+QQA',
hex: 'f90400',
roundtrip: true,
decoded: 0.00006103515625
},
{
cbor: '+cQA',
hex: 'f9c400',
roundtrip: false,
decoded: -4
},
{
cbor: '+8AQZmZmZmZm',
hex: 'fbc010666666666666',
roundtrip: true,
decoded: -4.1
},
{
cbor: '+XwA',
hex: 'f97c00',
roundtrip: true,
diagnostic: Infinity
},
{
cbor: '+X4A',
hex: 'f97e00',
roundtrip: true,
diagnostic: NaN
},
{
cbor: '+fwA',
hex: 'f9fc00',
roundtrip: true,
diagnostic: -Infinity
},
{
cbor: '+n+AAAA=',
hex: 'fa7f800000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+n/AAAA=',
hex: 'fa7fc00000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+v+AAAA=',
hex: 'faff800000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '+3/wAAAAAAAA',
hex: 'fb7ff0000000000000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+3/4AAAAAAAA',
hex: 'fb7ff8000000000000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+//wAAAAAAAA',
hex: 'fbfff0000000000000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '9A==',
hex: 'f4',
roundtrip: true,
decoded: false
},
{
cbor: '9Q==',
hex: 'f5',
roundtrip: true,
decoded: true
},
{
cbor: '9g==',
hex: 'f6',
roundtrip: true,
decoded: null
},
{
cbor: '9w==',
hex: 'f7',
roundtrip: true,
diagnostic: undefined
},
{
cbor: '8A==',
hex: 'f0',
roundtrip: true,
diagnostic: 'simple(16)',
error: /simple values are not supported/
},
{
cbor: '+Bg=',
hex: 'f818',
roundtrip: true,
diagnostic: 'simple(24)',
error: /simple values are not supported/
},
{
cbor: '+P8=',
hex: 'f8ff',
roundtrip: true,
diagnostic: 'simple(255)',
error: /simple values are not supported/
},
{
cbor: 'wHQyMDEzLTAzLTIxVDIwOjA0OjAwWg==',
hex: 'c074323031332d30332d32315432303a30343a30305a',
roundtrip: false,
diagnostic: '0("2013-03-21T20:04:00Z")'
},
{
cbor: 'wRpRS2ew',
hex: 'c11a514b67b0',
roundtrip: false,
diagnostic: '1(1363896240)'
},
{
cbor: 'wftB1FLZ7CAAAA==',
hex: 'c1fb41d452d9ec200000',
roundtrip: false,
diagnostic: '1(1363896240.5)'
},
{
cbor: '10QBAgME',
hex: 'd74401020304',
roundtrip: false,
diagnostic: '23(h\'01020304\')'
},
{
cbor: '2BhFZElFVEY=',
hex: 'd818456449455446',
roundtrip: false,
diagnostic: '24(h\'6449455446\')'
},
{
cbor: '2CB2aHR0cDovL3d3dy5leGFtcGxlLmNvbQ==',
hex: 'd82076687474703a2f2f7777772e6578616d706c652e636f6d',
roundtrip: false,
diagnostic: '32("http://www.example.com")'
},
{
cbor: 'QA==',
hex: '40',
roundtrip: true,
diagnostic: 'h\'\''
},
{
cbor: 'RAECAwQ=',
hex: '4401020304',
roundtrip: true,
diagnostic: 'h\'01020304\''
},
{
cbor: 'YA==',
hex: '60',
roundtrip: true,
decoded: ''
},
{
cbor: 'YWE=',
hex: '6161',
roundtrip: true,
decoded: 'a'
},
{
cbor: 'ZElFVEY=',
hex: '6449455446',
roundtrip: true,
decoded: 'IETF'
},
{
cbor: 'YiJc',
hex: '62225c',
roundtrip: true,
decoded: '"\\'
},
{
cbor: 'YsO8',
hex: '62c3bc',
roundtrip: true,
decoded: 'ü'
},
{
cbor: 'Y+awtA==',
hex: '63e6b0b4',
roundtrip: true,
decoded: '水'
},
{
cbor: 'ZPCQhZE=',
hex: '64f0908591',
roundtrip: true,
decoded: '\uD800\uDD51'
},
{
cbor: 'gA==',
hex: '80',
roundtrip: true,
decoded: []
},
{
cbor: 'gwECAw==',
hex: '83010203',
roundtrip: true,
decoded: [
1,
2,
3
]
},
{
cbor: 'gwGCAgOCBAU=',
hex: '8301820203820405',
roundtrip: true,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'mBkBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgYGBk=',
hex: '98190102030405060708090a0b0c0d0e0f101112131415161718181819',
roundtrip: true,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'oA==',
hex: 'a0',
roundtrip: true,
decoded: {}
},
{
cbor: 'ogECAwQ=',
hex: 'a201020304',
roundtrip: true,
diagnostic: '{1: 2, 3: 4}',
error: /non-string keys not supported/
},
{
cbor: 'omFhAWFiggID',
hex: 'a26161016162820203',
roundtrip: true,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhoWFiYWM=',
hex: '826161a161626163',
roundtrip: true,
decoded: [
'a',
{ b: 'c' }
]
},
{
cbor: 'pWFhYUFhYmFCYWNhQ2FkYURhZWFF',
hex: 'a56161614161626142616361436164614461656145',
roundtrip: true,
decoded: {
a: 'A',
b: 'B',
c: 'C',
d: 'D',
e: 'E'
}
},
{
cbor: 'X0IBAkMDBAX/',
hex: '5f42010243030405ff',
roundtrip: false,
diagnostic: '(_ h\'0102\', h\'030405\')',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'f2VzdHJlYWRtaW5n/w==',
hex: '7f657374726561646d696e67ff',
roundtrip: false,
decoded: 'streaming',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'n/8=',
hex: '9fff',
roundtrip: false,
decoded: []
},
{
cbor: 'nwGCAgOfBAX//w==',
hex: '9f018202039f0405ffff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwGCAgOCBAX/',
hex: '9f01820203820405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGCAgOfBAX/',
hex: '83018202039f0405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGfAgP/ggQF',
hex: '83019f0203ff820405',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwECAwQFBgcICQoLDA0ODxAREhMUFRYXGBgYGf8=',
hex: '9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff',
roundtrip: false,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'v2FhAWFinwID//8=',
hex: 'bf61610161629f0203ffff',
roundtrip: false,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhv2FiYWP/',
hex: '826161bf61626163ff',
roundtrip: false,
decoded: [
'a',
{ b: 'c' }
]
},
{
cbor: 'v2NGdW71Y0FtdCH/',
hex: 'bf6346756ef563416d7421ff',
roundtrip: false,
decoded: {
Fun: true,
Amt: -2
}
}
];
exports.fixtures = fixtures;
+24
View File
@@ -0,0 +1,24 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var token = require('../lib/token.js');
function dateDecoder(obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1');
}
return new Date(obj);
}
function dateEncoder(obj) {
if (!(obj instanceof Date)) {
throw new Error('expected Date for "Date" encoder');
}
return [
new token.Token(token.Type.tag, 0),
new token.Token(token.Type.string, obj.toISOString().replace(/\.000Z$/, 'Z'))
];
}
exports.dateDecoder = dateDecoder;
exports.dateEncoder = dateEncoder;
+348
View File
@@ -0,0 +1,348 @@
'use strict';
var chai = require('chai');
var child_process = require('child_process');
var process = require('process');
var path = require('path');
var os = require('os');
var url = require('url');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
var process__default = /*#__PURE__*/_interopDefaultLegacy(process);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
const {assert} = chai__default["default"];
const fixture1JsonString = '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}';
const fixture1JsonPrettyString = `{
"a": 1,
"b": [
2,
3
],
"smile": "😀"
}
`;
const fixture1HexString = 'a3616101616282020365736d696c6564f09f9880';
const fixture1Bin = fromHex(fixture1HexString);
const fixture1BinString = new TextDecoder().decode(fixture1Bin);
const fixture1DiagnosticString = `a3 # map(3)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`;
const fixture2HexString = 'a4616101616282020363627566440102036165736d696c6564f09f9880';
const fixture2DiagnosticString = `a4 # map(4)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
63 # string(3)
627566 # "buf"
44 # bytes(4)
01020361 # "\\x01\\x02\\x03a"
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`;
const binPath = path__default["default"].join(path__default["default"].dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('node-test/node-test-bin.js', document.baseURI).href)))), '../lib/bin.js');
function fromHex(hex) {
return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
}
async function execBin(cmd, stdin) {
return new Promise((resolve, reject) => {
const cp = child_process.exec(`"${ process__default["default"].execPath }" "${ binPath }" ${ cmd }`, (err, stdout, stderr) => {
if (err) {
err.stdout = stdout;
err.stderr = stderr;
return reject(err);
}
resolve({
stdout,
stderr
});
});
if (stdin != null) {
cp.on('spawn', () => {
cp.stdin.write(stdin);
cp.stdin.end();
});
}
});
}
describe('Bin', () => {
it('usage', async () => {
try {
await execBin('');
assert.fail('should have errored');
} catch (e) {
assert.strictEqual(e.stdout, '');
assert.strictEqual(e.stderr, `Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
}
});
it('bad cmd', async () => {
try {
await execBin('blip');
assert.fail('should have errored');
} catch (e) {
assert.strictEqual(e.stdout, '');
assert.strictEqual(e.stderr, `Unknown command: 'blip'
Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
}
});
it('help', async () => {
const {stdout, stderr} = await execBin('help');
assert.strictEqual(stdout, '');
assert.strictEqual(stderr, `Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
});
it('bin2diag (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2diag', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1DiagnosticString);
});
it('bin2hex (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2hex', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
it('bin2json (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2json', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it('bin2json pretty (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2json --pretty', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
for (const stdin of [
true,
false
]) {
if (os.platform() !== 'win32' || stdin) {
it(`diag2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2bin '${ fixture1DiagnosticString }'`) : await execBin('diag2bin', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`diag2hex${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2hex '${ fixture1DiagnosticString }'`) : await execBin('diag2hex', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
it(`diag2json${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2json '${ fixture1DiagnosticString }'`) : await execBin('diag2json', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it(`diag2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2json --pretty '${ fixture1DiagnosticString }'`) : await execBin('diag2json --pretty', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
}
it(`hex2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2bin ${ fixture1HexString }`) : await execBin('hex2bin', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`hex2diag${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2diag ${ fixture2HexString }`) : await execBin('hex2diag', fixture2HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture2DiagnosticString);
});
it(`hex2json${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2json ${ fixture1HexString }`) : await execBin('hex2json', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it(`hex2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2json --pretty ${ fixture1HexString }`) : await execBin('hex2json --pretty', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
it(`json2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2bin', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`json2diag${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2diag', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1DiagnosticString);
});
it(`json2hex${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`json2hex "${ fixture1JsonString.replace(/"/g, '\\"') }"`) : await execBin('json2hex', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
}
it('diag indenting', async () => {
const {stdout, stderr} = await execBin('json2diag', '{"a":[],"b":{},"c":{"a":1,"b":{"a":{"a":{}}}},"d":{"a":{"a":{"a":1},"b":2,"c":[]}},"e":[[[[{"a":{}}]]]],"f":1}');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `a6 # map(6)
61 # string(1)
61 # "a"
80 # array(0)
61 # string(1)
62 # "b"
a0 # map(0)
61 # string(1)
63 # "c"
a2 # map(2)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
a1 # map(1)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
64 # "d"
a1 # map(1)
61 # string(1)
61 # "a"
a3 # map(3)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
02 # uint(2)
61 # string(1)
63 # "c"
80 # array(0)
61 # string(1)
65 # "e"
81 # array(1)
81 # array(1)
81 # array(1)
81 # array(1)
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
66 # "f"
01 # uint(1)
`);
});
describe('diag length bytes', () => {
it('compact', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `77 # string(23)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
`);
});
it('1-byte', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `78 23 # string(35)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161616161616161616161 # "aaaaaaaaaaaa"
`);
});
it('2-byte', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `79 0100 # string(256)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161 # "aaa"
`);
});
});
it('diag non-utf8 and non-printable ascii', async () => {
const input = '7864f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c11e756338bd93865e645f1adec9b9c99ef407fbd4fc6859e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82f9f18c3d03418e35';
let {stdout, stderr} = await execBin(`hex2diag ${ input }`);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `78 64 # string(86)
f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b # "õ_øñ%\\x08¶>ò¿ì§Uzé\\x0dö1\\x1a^Ác\\x1b"
4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c # "J\\x1f¨C1\\x0bÙç\\x10ê¬å¡½×*пàIw\\x1c"
11e756338bd93865e645f1adec9b9c99ef407fbd4fc685 # "\\x11çV3\\x8bÙ8eæEñ\\xadì\\x9b\\x9c\\x99ï@\\x7f½OÆ\\x85"
9e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82 # "\\x9ey\\x04Å\\xad}ɽ\\x10¥Ì\\x16\\x97=[(ì\\x1amÔ=\\x9f\\x82"
f9f18c3d03418e35 # "ùñ\\x8c=\\x03A\\x8e5"
`);
({stdout, stderr} = await execBin('diag2hex', stdout));
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ input }\n`);
});
});
+4
View File
@@ -0,0 +1,4 @@
'use strict';
require('../lib/bin.js');
+158
View File
@@ -0,0 +1,158 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '00',
expected: 0,
type: 'uint8'
},
{
data: '02',
expected: 2,
type: 'uint8'
},
{
data: '18ff',
expected: 255,
type: 'uint8'
},
{
data: '1901f4',
expected: 500,
type: 'uint16'
},
{
data: '1900ff',
expected: 255,
type: 'uint16',
strict: false
},
{
data: '19ffff',
expected: 65535,
type: 'uint16'
},
{
data: '1a000000ff',
expected: 255,
type: 'uint32',
strict: false
},
{
data: '1a00010000',
expected: 65536,
type: 'uint32'
},
{
data: '1a000f4240',
expected: 1000000,
type: 'uint32'
},
{
data: '1aa5f702b3',
expected: 2784428723,
type: 'uint32'
},
{
data: '1b00000000000000ff',
expected: 255,
type: 'uint64',
strict: false
},
{
data: '1b0016db6db6db6db7',
expected: Number.MAX_SAFE_INTEGER / 1.4,
type: 'uint64'
},
{
data: '1b001fffffffffffff',
expected: Number.MAX_SAFE_INTEGER,
type: 'uint64'
},
{
data: '1ba5f702b3a5f702b3',
expected: BigInt('11959030306112471731'),
type: 'uint64'
},
{
data: '1bffffffffffffffff',
expected: BigInt('18446744073709551615'),
type: 'uint64'
}
];
describe('uint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(data) === fixture.expected, `decode ${ fixture.type } ${ decode.decode(data) } != ${ fixture.expected }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.ok(decode.decode(data, { strict: true }) === fixture.expected, `decode ${ fixture.type }`);
}
});
}
});
it('should throw error', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('1ca5f702b3a5f702b3')), Error, 'CBOR decode error: encountered invalid minor (28) for major 0');
assert.throws(() => decode.decode(byteUtils.fromHex('1ba5f702b3a5f702')), Error, 'CBOR decode error: not enough data for type');
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(encode.encode(fixture.expected)) === fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('toobig', () => {
it('bigger than 64-bit', () => {
assert.doesNotThrow(() => encode.encode(BigInt('18446744073709551615')));
assert.throws(() => encode.encode(BigInt('18446744073709551616')), /BigInt larger than allowable range/);
});
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode.decode(data, { allowBigInt: false }), /safe integer range/);
} else {
assert.ok(decode.decode(data, { allowBigInt: false }) === fixture.expected, `decode ${ fixture.type }`);
}
}
});
});
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected);
it(`should encode ${ small }n`, () => {
assert.strictEqual(byteUtils.toHex(encode.encode(BigInt(small))), fixture.data, `encode ${ small }`);
});
}
}
});
});
+152
View File
@@ -0,0 +1,152 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '20',
expected: -1,
type: 'negint8'
},
{
data: '22',
expected: -3,
type: 'negint8'
},
{
data: '3863',
expected: -100,
type: 'negint8'
},
{
data: '38ff',
expected: -256,
type: 'negint8'
},
{
data: '3900ff',
expected: -256,
type: 'negint16',
strict: false
},
{
data: '3901f4',
expected: -501,
type: 'negint16'
},
{
data: '3a000000ff',
expected: -256,
type: 'negint32',
strict: false
},
{
data: '3aa5f702b3',
expected: -2784428724,
type: 'negint32'
},
{
data: '3b00000000000000ff',
expected: -256,
type: 'negint32',
strict: false
},
{
data: '3b0016db6db6db6db7',
expected: Number.MIN_SAFE_INTEGER / 1.4 - 1,
type: 'negint64'
},
{
data: '3b001ffffffffffffe',
expected: Number.MIN_SAFE_INTEGER,
type: 'negint64'
},
{
data: '3b001fffffffffffff',
expected: BigInt('-9007199254740992'),
type: 'negint64'
},
{
data: '3b0020000000000000',
expected: BigInt('-9007199254740993'),
type: 'negint64'
},
{
data: '3ba5f702b3a5f702b3',
expected: BigInt('-11959030306112471732'),
type: 'negint64'
},
{
data: '3bffffffffffffffff',
expected: BigInt('-18446744073709551616'),
type: 'negint64'
}
];
describe('negint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(data) === fixture.expected, `decode ${ fixture.type } (${ decode.decode(data) } != ${ fixture.expected })`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.strictEqual(decode.decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
}
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode.decode(encode.encode(fixture.expected)) === fixture.expected, `roundtrip ${ fixture.type }`);
});
}
});
describe('toobig', () => {
it('bigger than 64-bit', () => {
assert.doesNotThrow(() => encode.encode(BigInt('-18446744073709551616')));
assert.throws(() => encode.encode(BigInt('-18446744073709551617')), /BigInt larger than allowable range/);
});
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode.decode(data, { allowBigInt: false }), /safe integer range/);
} else {
assert.ok(decode.decode(data, { allowBigInt: false }) === fixture.expected, `decode ${ fixture.type }`);
}
}
});
});
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected);
it(`should encode ${ small }n`, () => {
assert.strictEqual(byteUtils.toHex(encode.encode(BigInt(small))), fixture.data, `encode ${ small }`);
});
}
}
});
});
+254
View File
@@ -0,0 +1,254 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '40',
expected: '',
type: 'bytes'
},
{
data: '41a1',
expected: 'a1',
type: 'bytes'
},
{
data: '5801a1',
expected: 'a1',
type: 'bytes',
strict: false
},
{
data: '58ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 8-bit length'
},
{
data: '5900ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 16-bit length',
strict: false
},
{
data: '5a000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 32-bit length',
strict: false
},
{
data: '5b00000000000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 64-bit length',
strict: false
}
];
(() => {
function rnd(length) {
return new Uint8Array(Array.from({ length }, () => Math.floor(Math.random() * 255)));
}
const expected16 = rnd(256);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('590100'),
...expected16
]),
expected: expected16,
type: 'bytes',
label: 'long bytes, 16-bit length strict-compat'
});
const expected32 = rnd(65536);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('5a00010000'),
...expected32
]),
expected: expected32,
type: 'bytes',
label: 'long bytes, 32-bit length strict-compat'
});
})();
describe('bytes', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
let actual = decode.decode(data);
assert.strictEqual(byteUtils.toHex(actual), byteUtils.toHex(byteUtils.fromHex(fixture.expected)), `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
actual = decode.decode(data, { strict: true });
assert.strictEqual(byteUtils.toHex(actual), byteUtils.toHex(byteUtils.fromHex(fixture.expected)), `decode ${ fixture.type } strict`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('5ba5f702b3a5f702b3000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe')), /CBOR decode error: 64-bit integer bytes lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large bytes ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
});
continue;
}
const data = byteUtils.fromHex(fixture.expected);
const expectedHex = byteUtils.toHex(fixture.data);
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(() => encode.encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/);
} else if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type }`);
}
});
}
});
describe('typedarrays', () => {
const cases = [
{
obj: Uint8Array.from([
1,
2,
3
]),
hex: '43010203'
},
{
obj: Uint8ClampedArray.from([
1,
2,
3
]),
hex: '43010203'
},
{
obj: Uint16Array.from([
1,
2,
3
]),
hex: '46010002000300'
},
{
obj: Uint32Array.from([
1,
2,
3
]),
hex: '4c010000000200000003000000'
},
{
obj: Int8Array.from([
1,
2,
-3
]),
hex: '430102fd'
},
{
obj: Int16Array.from([
1,
2,
-3
]),
hex: '4601000200fdff'
},
{
obj: Int32Array.from([
1,
2,
-3
]),
hex: '4c0100000002000000fdffffff'
},
{
obj: Float32Array.from([
1,
2,
-3
]),
hex: '4c0000803f00000040000040c0'
},
{
obj: Float64Array.from([
1,
2,
-3
]),
hex: '5818000000000000f03f000000000000004000000000000008c0'
},
{
obj: BigUint64Array.from([
BigInt(1),
BigInt(2),
BigInt(3)
]),
hex: '5818010000000000000002000000000000000300000000000000'
},
{
obj: BigInt64Array.from([
BigInt(1),
BigInt(2),
BigInt(-3)
]),
hex: '581801000000000000000200000000000000fdffffffffffffff'
},
{
obj: new DataView(Uint8Array.from([
1,
2,
3
]).buffer),
hex: '43010203'
},
{
obj: Uint8Array.from([
1,
2,
3
]).buffer,
hex: '43010203'
}
];
for (const testCase of cases) {
it(testCase.obj.constructor.name, () => {
assert.equal(byteUtils.toHex(encode.encode(testCase.obj)), testCase.hex);
const decoded = decode.decode(byteUtils.fromHex(testCase.hex));
assert.instanceOf(decoded, Uint8Array);
assert.equal(byteUtils.toHex(decoded), byteUtils.toHex(testCase.obj));
});
}
});
if (byteUtils.useBuffer) {
describe('buffer', () => {
it('can encode Node.js Buffers', () => {
const obj = global.Buffer.from([
1,
2,
3
]);
assert.equal(byteUtils.toHex(encode.encode(obj)), '43010203');
const decoded = decode.decode(byteUtils.fromHex('43010203'));
assert.instanceOf(decoded, Uint8Array);
assert.equal(byteUtils.toHex(decoded), byteUtils.toHex(obj));
});
});
}
});
+144
View File
@@ -0,0 +1,144 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '60',
expected: '',
type: 'string'
},
{
data: '6161',
expected: 'a',
type: 'string'
},
{
data: '780161',
expected: 'a',
type: 'string',
strict: false
},
{
data: '6c48656c6c6f20776f726c6421',
expected: 'Hello world!',
type: 'string'
},
{
data: '6fc48c6175657320c39f76c49b746521',
expected: 'Čaues ßvěte!',
type: 'string'
},
{
data: '78964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 8-bit length'
},
{
data: '7900964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 16-bit length',
strict: false
},
{
data: '7a000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 32-bit length',
strict: false
},
{
data: '7b00000000000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 64-bit length',
strict: false
}
];
(() => {
function rnd(length) {
const sa = [];
let l = 0;
while (l < length) {
const ascii = length - l < 3;
const base = ascii ? 32 : 126976;
const max = ascii ? 126 : 130816;
const cc = Math.floor(Math.random() * (max - base)) + base;
const s = String.fromCharCode(cc);
l += new TextEncoder().encode(s).length;
sa.push(s);
}
return sa.join('');
}
const expected16 = rnd(256);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('790100'),
...new TextEncoder().encode(expected16)
]),
expected: expected16,
type: 'string',
label: 'long string, 16-bit length strict-compat'
});
const expected32 = rnd(65536);
fixtures.push({
data: new Uint8Array([
...byteUtils.fromHex('7a00010000'),
...new TextEncoder().encode(expected32)
]),
expected: expected32,
type: 'string',
label: 'long string, 32-bit length strict-compat'
});
})();
describe('string', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
let actual = decode.decode(data);
assert.strictEqual(actual, fixture.expected, `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
actual = decode.decode(data, { strict: true });
assert.strictEqual(actual, fixture.expected, `decode ${ fixture.type } strict`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('7ba5f702b3a5f702b34c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e')), /CBOR decode error: 64-bit integer string lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large string ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
});
continue;
}
const data = fixture.expected;
const expectedHex = byteUtils.toHex(fixture.data);
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(() => encode.encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/);
} else if (fixture.strict === false) {
assert.notStrictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(data)), expectedHex, `encode ${ fixture.type }`);
}
});
}
});
});
+200
View File
@@ -0,0 +1,200 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '80',
expected: [],
type: 'array empty'
},
{
data: '8102',
expected: [2],
type: 'array 1 compact uint'
},
{
data: '8118ff',
expected: [255],
type: 'array 1 uint8'
},
{
data: '811901f4',
expected: [500],
type: 'array 1 uint16'
},
{
data: '811a00010000',
expected: [65536],
type: 'array 1 uint32'
},
{
data: '811b00000000000000ff',
expected: [255],
type: 'array 1 uint64',
strict: false
},
{
data: '811b0016db6db6db6db7',
expected: [Number.MAX_SAFE_INTEGER / 1.4],
type: 'array 1 uint64'
},
{
data: '811b001fffffffffffff',
expected: [Number.MAX_SAFE_INTEGER],
type: 'array 1 uint64'
},
{
data: '8403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints'
},
{
data: '8c1b0016db6db6db6db71a000100001901f40200202238ff3aa5f702b33b0016db6db6db6db74261316fc48c6175657320c39f76c49b746521',
expected: [
Number.MAX_SAFE_INTEGER / 1.4,
65536,
500,
2,
0,
-1,
-3,
-256,
-2784428724,
Number.MIN_SAFE_INTEGER / 1.4 - 1,
new TextEncoder().encode('a1'),
'Čaues ßvěte!'
],
type: 'array mixed terminals',
label: '[]'
},
{
data: '8265617272617982626f66820582666e657374656482666172726179736121',
expected: [
'array',
[
'of',
[
5,
[
'nested',
[
'arrays',
'!'
]
]
]
]
],
type: 'array nested'
},
{
data: '980403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length8',
strict: false
},
{
data: '99000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length16',
strict: false
},
{
data: '9a0000000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length32',
strict: false
},
{
data: '9b000000000000000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length64',
strict: false
}
];
describe('array', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(data), fixture.expected, `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.deepStrictEqual(decode.decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('9ba5f702b3a5f7020403040506')), /CBOR decode error: 64-bit integer array lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(encode.encode.bind(null, fixture.expected), Error, /^CBOR encode error: number too large to encode \(\d+\)$/);
} else if (fixture.strict === false) {
assert.notDeepEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(encode.encode(fixture.expected)), fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('9f616f6174ff')), [
'o',
't'
]);
});
it('can switch off indefinite length support', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('9f616f6174ff'), { allowIndefinite: false }), /indefinite/);
});
});
});
+667
View File
@@ -0,0 +1,667 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: 'a0',
expected: {},
type: 'map empty'
},
{
data: 'a0',
expected: new Map(),
type: 'map empty (useMaps)',
useMaps: true
},
{
data: 'a1616101',
expected: { a: 1 },
type: 'map 1 pair'
},
{
data: 'a161316161',
expected: { 1: 'a' },
type: 'map 1 pair (rev)'
},
{
data: 'a1016161',
expected: toMap([[
1,
'a'
]]),
type: 'map 1 pair (int key as Map w/ useMaps)',
useMaps: true
},
{
data: 'a243010203633132334302030463323334',
expected: toMap([
[
Uint8Array.from([
1,
2,
3
]),
'123'
],
[
Uint8Array.from([
2,
3,
4
]),
'234'
]
]),
type: 'map 2 pair (bytes keys Map w/ useMaps)',
useMaps: true
},
{
data: 'a1666f626a656374a16477697468a26134666e6573746564676f626a65637473a161216121',
expected: {
object: {
with: {
4: 'nested',
objects: { '!': '!' }
}
}
},
type: 'map nested'
},
{
data: 'a1666f626a656374a16477697468a204666e6573746564676f626a65637473a161216121',
expected: toMap([[
'object',
toMap([[
'with',
toMap([
[
4,
'nested'
],
[
'objects',
toMap([[
'!',
'!'
]])
]
])
]])
]]),
type: 'map nested w/ useMaps',
useMaps: true
},
{
data: 'ae636f6e651b0016db6db6db6db763736978206374656e3b0016db6db6db6db76374776f1a0001000064666976650064666f757202646e696e653aa5f702b365656967687438ff65736576656e226574687265651901f466656c6576656e426131667477656c76656fc48c6175657320c39f76c49b74652168666f75727465656ea4616664666f7572616f016174026274680368746869727465656e840203046466697665',
encode: {
one: Number.MAX_SAFE_INTEGER / 1.4,
two: 65536,
three: 500,
four: 2,
five: 0,
six: -1,
seven: -3,
eight: -256,
nine: -2784428724,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
thirteen: [
2,
3,
4,
'five'
],
fourteen: {
o: 1,
t: 2,
th: 3,
f: 'four'
}
},
expected: {
one: Number.MAX_SAFE_INTEGER / 1.4,
six: -1,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
two: 65536,
five: 0,
four: 2,
nine: -2784428724,
eight: -256,
seven: -3,
three: 500,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
fourteen: {
f: 'four',
o: 1,
t: 2,
th: 3
},
thirteen: [
2,
3,
4,
'five'
]
},
type: 'map with complex entries',
label: '{}'
},
{
data: 'ad01636f6e65026374776f1901f46c666976652068756e647265641902586b7369782068756e647265641a00010000636269671b0016db6db6db6db76662696767657220696d696e7573206f6e6521696d696e75732074776f38ff781f6d696e75732074776f2068756e6472656420616e64206669667479207369783901f4781a6d696e757820666976652068756e6472656420616e64206f6e653901f5781a6d696e757820666976652068756e6472656420616e642074776f3aa5f702b367626967206e65673b0016db6db6db6db76a626967676572206e6567',
encode: toMap([
[
2,
'two'
],
[
1,
'one'
],
[
-2,
'minus two'
],
[
-1,
'minus one'
],
[
600,
'six hundred'
],
[
500,
'five hundred'
],
[
-256,
'minus two hundred and fifty six'
],
[
-502,
'minux five hundred and two'
],
[
-501,
'minux five hundred and one'
],
[
65536,
'big'
],
[
-2784428724,
'big neg'
],
[
6433713753386423,
'bigger'
],
[
-6433713753386424,
'bigger neg'
]
]),
expected: toMap([
[
1,
'one'
],
[
2,
'two'
],
[
500,
'five hundred'
],
[
600,
'six hundred'
],
[
65536,
'big'
],
[
6433713753386423,
'bigger'
],
[
-1,
'minus one'
],
[
-2,
'minus two'
],
[
-256,
'minus two hundred and fifty six'
],
[
-501,
'minux five hundred and one'
],
[
-502,
'minux five hundred and two'
],
[
-2784428724,
'big neg'
],
[
-6433713753386424,
'bigger neg'
]
]),
type: 'map with ints and negints',
useMaps: true
},
{
data: 'a44104636f6e65430102026374776f430102036574687265654301020464666f7572',
encode: toMap([
[
Uint8Array.from([
1,
2,
3
]),
'three'
],
[
Uint8Array.from([4]),
'one'
],
[
Uint8Array.from([
1,
2,
4
]),
'four'
],
[
Uint8Array.from([
1,
2,
2
]),
'two'
]
]),
expected: toMap([
[
Uint8Array.from([4]),
'one'
],
[
Uint8Array.from([
1,
2,
2
]),
'two'
],
[
Uint8Array.from([
1,
2,
3
]),
'three'
],
[
Uint8Array.from([
1,
2,
4
]),
'four'
]
]),
type: 'map with bytes keys',
useMaps: true
},
{
data: 'b801616101',
expected: { a: 1 },
type: 'map 1 pair, length8',
strict: false
},
{
data: 'b90001616101',
expected: { a: 1 },
type: 'map 1 pair, length16',
strict: false
},
{
data: 'ba00000001616101',
expected: { a: 1 },
type: 'map 1 pair, length32',
strict: false
},
{
data: 'bb0000000000000001616101',
expected: { a: 1 },
type: 'map 1 pair, length64',
strict: false
}
];
function toMap(arr) {
const m = new Map();
for (const [key, value] of arr) {
m.set(key, value);
}
return m;
}
function entries(map) {
function nest(a) {
for (const e of a) {
e[0] = entries(e[0]);
e[1] = entries(e[1]);
}
return a;
}
if (Object.getPrototypeOf(map) === Map.prototype) {
return nest([...map.entries()]);
}
if (typeof map === 'object') {
return nest([...Object.entries(map)]);
}
return map;
}
describe('map', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
let options = fixture.useMaps ? { useMaps: true } : undefined;
const decoded = decode.decode(data, options);
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(decoded), Map.prototype, 'is Map');
} else {
assert.isObject(decoded, 'is object');
}
assert.deepStrictEqual(entries(decoded), entries(fixture.expected), `decode ${ fixture.type }`);
options = Object.assign({ strict: true }, options);
if (fixture.strict === false) {
assert.throws(() => decode.decode(data, options), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.deepStrictEqual(entries(decode.decode(data, options)), entries(fixture.expected), `decode ${ fixture.type }`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('bba5f702b3a5f70201616101')), /CBOR decode error: 64-bit integer map lengths not supported/);
});
}
it('errors', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a1016161')), /non-string keys not supported \(got number\)/);
});
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
const toEncode = fixture.encode || fixture.expected;
if (fixture.unsafe) {
assert.throws(encode.encode.bind(null, toEncode), Error, /^CBOR encode error: number too large to encode \(\d+\)$/);
} else if (fixture.strict === false || fixture.roundtrip === false) {
assert.notDeepEqual(byteUtils.toHex(encode.encode(toEncode)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(byteUtils.toHex(encode.encode(toEncode)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false && fixture.roundtrip !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
const toEncode = fixture.encode || fixture.expected;
const options = fixture.useMaps ? { useMaps: true } : undefined;
const rt = decode.decode(encode.encode(toEncode), options);
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(rt), Map.prototype, 'is Map');
} else {
assert.isObject(rt, 'is object');
}
assert.deepStrictEqual(entries(rt), entries(fixture.expected), `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('bf616f01617402ff')), {
o: 1,
t: 2
});
});
it('can switch off indefinite length support', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('bf616f01617402ff'), { allowIndefinite: false }), /indefinite/);
});
});
describe('sorting', () => {
it('sorts int map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
1,
1
],
[
2,
2
]
]))), 'a201010202');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
2,
1
],
[
1,
2
]
]))), 'a201020201');
});
it('sorts negint map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
-1,
1
],
[
-2,
2
]
]))), 'a220012102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
-2,
1
],
[
-1,
2
]
]))), 'a220022101');
});
it('sorts bytes map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
]
]))), 'a24201020142020102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
2,
1
]),
1
],
[
Uint8Array.from([
1,
2
]),
2
]
]))), 'a24201020242020101');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
],
[
Uint8Array.from([200]),
3
]
]))), 'a341c8034201020142020102');
});
it('sorts bytes map keys', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
]
]))), 'a24201020142020102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
2,
1
]),
1
],
[
Uint8Array.from([
1,
2
]),
2
]
]))), 'a24201020242020101');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
],
[
Uint8Array.from([200]),
3
]
]))), 'a341c8034201020142020102');
});
it('sorts array map keys (length only)', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
[1],
1
],
[
[
1,
1
],
2
]
]))), 'a281010182010102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
[
1,
1
],
1
],
[
[1],
2
]
]))), 'a281010282010101');
});
it('sorts map map keys (length only)', () => {
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
{ a: 1 },
1
],
[
{
a: 1,
b: 1
},
2
]
]))), 'a2a161610101a261610161620102');
assert.strictEqual(byteUtils.toHex(encode.encode(new Map([
[
{
a: 1,
b: 1
},
1
],
[
{ a: 1 },
2
]
]))), 'a2a161610102a261610161620101');
});
});
});
+75
View File
@@ -0,0 +1,75 @@
'use strict';
var chai = require('chai');
var token = require('../lib/token.js');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var common = require('./common.js');
var encode = require('../lib/encode.js');
var decode = require('../lib/decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
function Uint16ArrayDecoder(obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 23');
}
const u8a = byteUtils.fromHex(obj);
return new Uint16Array(u8a.buffer, u8a.byteOffset, u8a.length / 2);
}
function Uint16ArrayEncoder(obj) {
if (!(obj instanceof Uint16Array)) {
throw new Error('expected Uint16Array for "Uint16Array" encoder');
}
return [
new token.Token(token.Type.tag, 23),
new token.Token(token.Type.string, byteUtils.toHex(obj))
];
}
describe('tag', () => {
it('date', () => {
assert.throws(() => encode.encode({ d: new Date() }), /unsupported type: Date/);
assert.equal(byteUtils.toHex(encode.encode(new Date('2013-03-21T20:04:00Z'), { typeEncoders: { Date: common.dateEncoder } })), 'c074323031332d30332d32315432303a30343a30305a');
const decodedDate = decode.decode(byteUtils.fromHex('c074323031332d30332d32315432303a30343a30305a'), { tags: { 0: common.dateDecoder } });
assert.instanceOf(decodedDate, Date);
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString());
});
it('Uint16Array as hex/23 (overide existing type)', () => {
assert.equal(byteUtils.toHex(encode.encode(Uint16Array.from([
1,
2,
3
]), { typeEncoders: { Uint16Array: Uint16ArrayEncoder } })), 'd76c303130303032303030333030');
const decoded = decode.decode(byteUtils.fromHex('d76c303130303032303030333030'), { tags: { 23: Uint16ArrayDecoder } });
assert.instanceOf(decoded, Uint16Array);
assert.equal(byteUtils.toHex(decoded), byteUtils.toHex(Uint16Array.from([
1,
2,
3
])));
});
it('tag int too large', () => {
const verify = (hex, strict) => {
if (!strict) {
assert.throws(() => decode.decode(byteUtils.fromHex(hex), {
tags: { 8: common.dateDecoder },
strict: true
}), /integer encoded in more bytes than necessary/);
}
const decodedDate = decode.decode(byteUtils.fromHex(hex), {
tags: { 8: common.dateDecoder },
strict
});
assert.instanceOf(decodedDate, Date);
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString());
};
verify('c874323031332d30332d32315432303a30343a30305a', true);
verify('d80874323031332d30332d32315432303a30343a30305a', false);
verify('d9000874323031332d30332d32315432303a30343a30305a', false);
verify('da0000000874323031332d30332d32315432303a30343a30305a', false);
verify('db000000000000000874323031332d30332d32315432303a30343a30305a', false);
});
});
+253
View File
@@ -0,0 +1,253 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const fixtures = [
{
data: '8601f5f4f6f720',
expected: [
1,
true,
false,
null,
undefined,
-1
],
type: 'array of float specials'
},
{
data: 'f93800',
expected: 0.5,
type: 'float16'
},
{
data: 'f9b800',
expected: -0.5,
type: 'float16'
},
{
data: 'fa33c00000',
expected: 8.940696716308594e-8,
type: 'float32'
},
{
data: 'fab3c00000',
expected: -8.940696716308594e-8,
type: 'float32'
},
{
data: 'fb3ff199999999999a',
expected: 1.1,
type: 'float64'
},
{
data: 'fbbff199999999999a',
expected: -1.1,
type: 'float64'
},
{
data: 'fb3ff1c71c71c71c72',
expected: 1.1111111111111112,
type: 'float64'
},
{
data: 'fb0000000000000002',
expected: 1e-323,
type: 'float64'
},
{
data: 'fb8000000000000002',
expected: -1e-323,
type: 'float64'
},
{
data: 'fb3fefffffffffffff',
expected: 0.9999999999999999,
type: 'float64'
},
{
data: 'fbbfefffffffffffff',
expected: -0.9999999999999999,
type: 'float64'
},
{
data: 'f97c00',
expected: Infinity,
type: 'Infinity'
},
{
data: 'fb7ff0000000000000',
expected: Infinity,
type: 'Infinity',
strict: false
},
{
data: 'f9fc00',
expected: -Infinity,
type: '-Infinity'
},
{
data: 'fbfff0000000000000',
expected: -Infinity,
type: '-Infinity',
strict: false
},
{
data: 'f97e00',
expected: NaN,
type: 'NaN'
},
{
data: 'f97ff8',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fa7ff80000',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb7ff8000000000000',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb7ff8cafedeadbeef',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb40f4241a31a5a515',
expected: 82497.63712086187,
type: 'float64'
}
];
describe('float', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = byteUtils.fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(data), fixture.expected, `decode ${ fixture.type }`);
assert.deepStrictEqual(decode.decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
});
}
});
it('error', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('f80000')), Error, 'simple values are not supported');
assert.throws(() => decode.decode(byteUtils.fromHex('f900')), Error, 'not enough data for float16');
assert.throws(() => decode.decode(byteUtils.fromHex('fa0000')), Error, 'not enough data for float32');
assert.throws(() => decode.decode(byteUtils.fromHex('fb00000000')), Error, 'not enough data for float64');
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
assert.strictEqual(byteUtils.toHex(encode.encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
});
}
}
});
describe('encode float64', () => {
for (const fixture of fixtures) {
if (fixture.type.startsWith('float')) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
const encoded = encode.encode(fixture.expected, { float64: true });
assert.strictEqual(encoded.length, 9);
assert.strictEqual(encoded[0], 251);
assert.strictEqual(decode.decode(encoded), fixture.expected, `encode float64 ${ fixture.type }`);
});
}
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.deepStrictEqual(decode.decode(encode.encode(fixture.expected)), fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('indefinite length switch fails on BREAK', () => {
assert.throws(() => decode.decode(Uint8Array.from([
131,
1,
2,
255
])), /unexpected break to lengthed array/);
assert.throws(() => decode.decode(Uint8Array.from([
131,
1,
2,
255
]), { allowIndefinite: false }), /indefinite/);
});
it('can switch off undefined support', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7')), undefined);
assert.throws(() => decode.decode(byteUtils.fromHex('f7'), { allowUndefined: false }), /undefined/);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7')), [
1,
2,
undefined
]);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f7'), { allowUndefined: false }), /undefined/);
});
it('can coerce undefined to null', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7'), { coerceUndefinedToNull: false }), undefined);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('f7'), { coerceUndefinedToNull: true }), null);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7'), { coerceUndefinedToNull: false }), [
1,
2,
undefined
]);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f7'), { coerceUndefinedToNull: true }), [
1,
2,
null
]);
});
it('can switch off Infinity support', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f97c00')), [
1,
2,
Infinity
]);
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f9fc00')), [
1,
2,
-Infinity
]);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f97c00'), { allowInfinity: false }), /Infinity/);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f9fc00'), { allowInfinity: false }), /Infinity/);
for (const fixture of fixtures.filter(f => f.type.endsWith('Infinity'))) {
assert.throws(() => decode.decode(byteUtils.fromHex(fixture.data), { allowInfinity: false }), /Infinity/);
}
});
it('can switch off NaN support', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('830102f97e00')), [
1,
2,
NaN
]);
assert.throws(() => decode.decode(byteUtils.fromHex('830102f97e00'), { allowNaN: false }), /NaN/);
for (const fixture of fixtures.filter(f => f.type === 'NaN')) {
assert.throws(() => decode.decode(byteUtils.fromHex(fixture.data), { allowNaN: false }), /NaN/);
}
});
});
});
+91
View File
@@ -0,0 +1,91 @@
'use strict';
var chai = require('chai');
var bl = require('../lib/bl.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
describe('Internal bytes list', () => {
describe('push', () => {
it('push bits', () => {
const bl$1 = new bl.Bl(10);
const expected = [];
for (let i = 0; i < 25; i++) {
bl$1.push([i + 1]);
expected.push(i + 1);
}
assert.deepEqual([...bl$1.toBytes()], expected);
});
for (let i = 4; i < 21; i++) {
it(`push Bl(${ i })`, () => {
const bl$1 = new bl.Bl(i);
const expected = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
100,
110,
120,
11,
12,
130,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
];
for (let i = 0; i < 5; i++) {
bl$1.push([i + 1]);
}
bl$1.push(Uint8Array.from([
6,
7,
8,
9,
10
]));
bl$1.push([100]);
bl$1.push(Uint8Array.from([
110,
120
]));
bl$1.push(Uint8Array.from([
11,
12
]));
bl$1.push([130]);
bl$1.push(Uint8Array.from([
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]));
assert.deepEqual([...bl$1.toBytes()], expected);
});
}
});
});
+98
View File
@@ -0,0 +1,98 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var taglib = require('../taglib.js');
var byteUtils = require('../lib/byte-utils.js');
var appendix_a = require('./appendix_a.js');
var decode = require('../lib/decode.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
const tags = [];
const typeEncoders = {};
tags[0] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1');
}
return `0("${ new Date(obj).toISOString().replace(/\.000Z$/, 'Z') }")`;
};
tags[1] = function (obj) {
if (typeof obj !== 'number') {
throw new Error('expected number for tag 1');
}
return `1(${ obj })`;
};
tags[2] = taglib.bigIntDecoder;
typeEncoders.bigint = taglib.bigIntEncoder;
tags[3] = taglib.bigNegIntDecoder;
tags[23] = function (obj) {
if (!(obj instanceof Uint8Array)) {
throw new Error('expected byte array for tag 23');
}
return `23(h'${ byteUtils.toHex(obj) }')`;
};
tags[24] = function (obj) {
return tags[23](obj).replace(/^23/, '24');
};
tags[32] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 32');
}
;
(() => new URL(obj))();
return `32("${ obj }")`;
};
describe('cbor/test-vectors', () => {
let i = 0;
for (const fixture of appendix_a.fixtures) {
const u8a = byteUtils.fromHex(fixture.hex);
let expected = fixture.decoded !== undefined ? fixture.decoded : fixture.diagnostic;
if (typeof expected === 'string' && expected.startsWith('h\'')) {
expected = byteUtils.fromHex(expected.replace(/(^h)'|('$)/g, ''));
}
it(`test vector #${ i }: ${ inspect(expected).replace(/\n\s*/g, '') }`, () => {
if (fixture.error) {
assert.throws(() => decode.decode(u8a, { tags }), fixture.error);
} else {
if (fixture.noTagDecodeError) {
assert.throws(() => decode.decode(u8a), fixture.noTagDecodeError);
}
let actual = decode.decode(u8a, { tags });
if (typeof actual === 'bigint') {
actual = inspect(actual);
}
if (typeof expected === 'bigint') {
expected = inspect(expected);
}
assert.deepEqual(actual, expected);
if (fixture.roundtrip) {
if (fixture.noTagEncodeError) {
assert.throws(() => encode.encode(decode.decode(u8a, { tags })), fixture.noTagEncodeError);
}
const reencoded = encode.encode(decode.decode(u8a, { tags }), { typeEncoders });
assert.equal(byteUtils.toHex(reencoded), fixture.hex);
}
}
});
i++;
}
it.skip('encode w/ tags', () => {
});
});
function inspect(o) {
if (typeof o === 'string') {
return `'${ o }'`;
}
if (o instanceof Uint8Array) {
return `Uint8Array<${ o.join(',') }>`;
}
if (o == null || typeof o !== 'object') {
return String(o);
}
return JSON.stringify(o);
}
+77
View File
@@ -0,0 +1,77 @@
'use strict';
var chai = require('chai');
require('../cborg.js');
var byteUtils = require('../lib/byte-utils.js');
var decode = require('../lib/decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
describe('decode errors', () => {
it('not Uint8Array', () => {
for (const arg of [
true,
false,
null,
undefined,
'string',
{ obj: 'ect' },
{},
['array'],
[],
[
1,
2,
3
],
0,
100,
1.1,
-1,
Symbol.for('nope')
]) {
assert.throws(() => decode.decode(arg), /CBOR decode error.*must be a Uint8Array/);
}
});
it('no data', () => {
assert.throws(() => decode.decode(new Uint8Array('')), /CBOR decode error.*content/);
});
it('break only', () => {
assert.throws(() => decode.decode(new Uint8Array([255])), /CBOR decode error.*break/);
});
it('not enough map entries (value)', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a2616f016174')), /map.*not enough entries.*value/);
});
it('not enough map entries (key)', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a2616f01')), /map.*not enough entries.*key/);
});
it('break in lengthed map', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('a2616f01ff740f')), /unexpected break to lengthed map/);
});
it('not enough array entries', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('82616f')), /array.*not enough entries/);
});
it('break in lengthed array', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('82ff')), /unexpected break to lengthed array/);
});
it('no such decoder', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('82ff')), /unexpected break to lengthed array/);
});
it('too many terminals', () => {
assert.throws(() => decode.decode(byteUtils.fromHex('0101')), /too many terminals/);
});
it('rejectDuplicateMapKeys enabled on duplicate keys', () => {
assert.deepStrictEqual(decode.decode(byteUtils.fromHex('a3636261720363666f6f0163666f6f02')), {
foo: 2,
bar: 3
});
assert.throws(() => decode.decode(byteUtils.fromHex('a3636261720363666f6f0163666f6f02'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/);
assert.throws(() => decode.decode(byteUtils.fromHex('a3636261720363666f6f0163666f6f02'), {
useMaps: true,
rejectDuplicateMapKeys: true
}), /CBOR decode error: found repeat map key "foo"/);
});
});
+56
View File
@@ -0,0 +1,56 @@
'use strict';
var ipldGarbage = require('ipld-garbage');
require('../cborg.js');
var chai = require('chai');
var encode = require('../lib/encode.js');
var decode = require('../lib/decode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
describe('Fuzz round-trip', () => {
it('random objects', function () {
this.timeout(5000);
for (let i = 0; i < 1000; i++) {
const obj = ipldGarbage.garbage(300, { weights: { CID: 0 } });
const byts = encode.encode(obj);
const decoded = decode.decode(byts);
assert.deepEqual(decoded, obj);
}
});
it('circular references error', () => {
let obj = {};
obj.obj = obj;
assert.throws(() => encode.encode(obj), /circular references/);
obj = {
blip: [
1,
2,
{ blop: {} }
]
};
obj.blip[2].blop.boop = obj;
assert.throws(() => encode.encode(obj), /circular references/);
obj = {
blip: [
1,
2,
{ blop: {} }
]
};
obj.blip[2].blop.boop = obj.blip;
assert.throws(() => encode.encode(obj), /circular references/);
obj = {
blip: {},
bloop: {}
};
obj.bloop = obj.blip;
assert.doesNotThrow(() => encode.encode(obj));
const arr = [];
arr[0] = arr;
assert.throws(() => encode.encode(arr), /circular references/);
});
});
+281
View File
@@ -0,0 +1,281 @@
'use strict';
var chai = require('chai');
require('../lib/json/json.js');
var encode = require('../lib/json/encode.js');
var decode = require('../lib/json/decode.js');
const toBytes = str => new TextEncoder().encode(str);
function verifyRoundTrip(obj, sorting) {
const encoded = new TextDecoder().decode(encode.encode(obj, sorting === false ? { mapSorter: null } : undefined));
const json = JSON.stringify(obj);
chai.assert.strictEqual(encoded, json);
const decoded = decode.decode(toBytes(JSON.stringify(obj)));
chai.assert.deepStrictEqual(decoded, obj);
}
function verifyEncodedForm(testCase) {
const obj = JSON.parse(testCase);
const encoded = encode.encode(obj);
chai.assert.strictEqual(new TextDecoder().decode(encoded), JSON.stringify(obj));
const decoded = decode.decode(encoded);
chai.assert.deepStrictEqual(decoded, obj);
const decoded2 = decode.decode(toBytes(testCase));
chai.assert.deepStrictEqual(decoded2, obj);
}
describe('json basics', () => {
it('can round-trip basic literals', () => {
const testCases = [
'null',
'true',
'false',
'0',
'9007199254740991',
'-9007199254740991',
JSON.stringify(Number.MAX_VALUE),
JSON.stringify(Number.MIN_VALUE)
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
chai.assert.strictEqual(decode.decode(toBytes('1E1')), 10);
chai.assert.strictEqual(decode.decode(toBytes('0.1e1')), 1);
chai.assert.strictEqual(decode.decode(toBytes('1e-1')), 0.1);
chai.assert.strictEqual(decode.decode(toBytes('1e+00')), 1);
chai.assert.strictEqual(decode.decode(toBytes('10.0')), 10);
chai.assert.deepStrictEqual(decode.decode(toBytes('[-10.0,1.0,0.0,100.0]')), [
-10,
1,
0,
100
]);
verifyRoundTrip(true);
verifyRoundTrip(false);
verifyRoundTrip(null);
verifyRoundTrip(100);
verifyRoundTrip(-100);
verifyRoundTrip(1.11);
verifyRoundTrip(-100.11111);
verifyRoundTrip(11100000000);
verifyRoundTrip(1.0011111e-18);
});
it('handles large integers as BigInt', () => {
const verify = (inp, str) => {
if (str === undefined) {
str = String(inp);
}
chai.assert.strictEqual(decode.decode(toBytes(str), { allowBigInt: true }), inp);
chai.assert.strictEqual(decode.decode(toBytes(str)), parseFloat(str));
};
verify(Number.MAX_SAFE_INTEGER);
verify(-Number.MAX_SAFE_INTEGER);
verify(BigInt('9007199254740992'));
verify(BigInt('9007199254740993'));
verify(BigInt('11959030306112471731'));
verify(BigInt('18446744073709551615'));
verify(BigInt('9223372036854775807'));
verify(BigInt('-9007199254740992'));
verify(BigInt('-9007199254740993'));
verify(BigInt('-9223372036854776000'));
verify(BigInt('-11959030306112471732'));
verify(BigInt('-18446744073709551616'));
verify(-9007199254740992, '-9007199254740992.0');
verify(-9223372036854776000, '-9223372036854776000.0');
verify(-18446744073709552000, '-18446744073709551616.0');
});
it('can round-trip string literals', () => {
const testCases = [
JSON.stringify(''),
JSON.stringify(' '),
JSON.stringify('"'),
JSON.stringify('\\'),
JSON.stringify('\b\f\n\r\t'),
JSON.stringify('"'),
JSON.stringify('&#34; %22 0x22 034 &#x22;'),
'"\uD83D\uDE00"'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
chai.assert.strictEqual(decode.decode(toBytes('"/ & \\/"')), '/ & /');
verifyRoundTrip('this is a string');
verifyRoundTrip('this \uD834\uDD1E is a \u263A\u263A \u2663 string ̐ ̀\n\r');
verifyRoundTrip('');
verifyRoundTrip('foo\\bar\nbaz\tbop\rbing"bip\'bang');
});
it('can round-trip array literals', () => {
const testCases = [
'[]',
'[null]',
'[true, false]',
'[ \n 0,1, 2\n , 3,\n4] \n ',
'[-10.0, 1.0, 0.0, 100.0]',
'[["2 deep"]]'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
verifyRoundTrip([
1,
2,
3,
'string',
true,
4
]);
verifyRoundTrip([
1,
2,
3,
'string',
true,
[
'and',
'a',
'nested',
'array',
true
],
4
]);
});
it('can round-trip object literals', () => {
const testCases = [
'{}',
'\n {\n "\\b"\n :\n""\n }\n ',
'{"":""}',
'{"1":{"2":0,"3":"deep"}}'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
});
it('will sort map keys', () => {
const unsorted = {
one: 1,
two: 2,
three: 3.1,
str: 'string',
bool: true,
four: 4
};
verifyRoundTrip(unsorted, false);
chai.assert.strictEqual(new TextDecoder().decode(encode.encode(unsorted)), '{"bool":true,"four":4,"one":1,"str":"string","three":3.1,"two":2}');
});
it('can handle novel cases', () => {
chai.assert.strictEqual(decode.decode(toBytes('"this \\uD834\\uDD1E is a \\u263a\\u263a string"')), 'this \uD834\uDD1E is a \u263A\u263A string');
verifyRoundTrip({
one: 1,
two: 2,
three: 3.1,
str: 'string',
arr: [
'and',
'a',
'nested',
[],
'array',
[
true,
1
],
false
],
bool: true,
obj: {
nested: 'object',
a: [],
o: {}
},
four: 4
}, false);
verifyRoundTrip([
false,
[
{
'#nFzU': {},
'\\w>': -0.9441451951197325,
'\t\'': '\'JB+2Wg\tw"IrM*#e^L/d&4rrzUuwq(1mH6aVRredB&Bfs]S"KqK(Tz1Q"URBAfw',
'\n@FrfM': 'M[D]q&'
},
'J4>\'Xdc+u2$%',
4227406737130333
]
], false);
verifyRoundTrip([
0.12995619865708727,
-4973404279772543,
{
drG2: [true],
';#K^Qf>V': null,
'`2=': 'ecc<e/$+-.;U>Gr5RdZDJ\n5+:{=QHNN.tVVN~dX$FWFwu`6>"&=tW!*1*^\u263A)JFM1p|}&X.B|${*\\f@!w2\u263A+'
}
], false);
chai.assert.strictEqual(`${ decode.decode(encode.encode(9007199254740991)) }`, '9007199254740991');
chai.assert.strictEqual(`${ decode.decode(encode.encode(9007199254740992)) }`, '9007199254740992');
chai.assert.strictEqual(`${ decode.decode(encode.encode(900719925474099100n)) }`, '900719925474099100');
});
it('should throw on bad types', () => {
chai.assert.throws(() => encode.encode(new Uint8Array([
1,
2
])), /CBOR encode error: unsupported type: Uint8Array/);
chai.assert.throws(() => encode.encode({
boop: new Uint8Array([
1,
2
])
}), /CBOR encode error: unsupported type: Uint8Array/);
chai.assert.throws(() => encode.encode(undefined), /CBOR encode error: unsupported type: undefined/);
chai.assert.throws(() => encode.encode(new Map([
[
1,
2
],
[
2,
3
]
])), /CBOR encode error: non-string map keys are not supported/);
chai.assert.throws(() => encode.encode(new Map([
[
[
'foo',
'bar'
],
2
],
[
[
'bar',
'foo'
],
3
]
])), /CBOR encode error: complex map keys are not supported/);
});
it('should throw on bad decode failure modes', () => {
chai.assert.throws(() => decode.decode(toBytes('{"a":1 & "b":2}')), 'CBOR decode error: unexpected character at position 7, was expecting object delimiter but found \'&\'');
chai.assert.throws(() => decode.decode(toBytes('{"a":1,"b"!2}')), 'CBOR decode error: unexpected character at position 10, was expecting key/value delimiter \':\' but found \'!\'');
chai.assert.throws(() => decode.decode(toBytes('[1,2&3]')), 'CBOR decode error: unexpected character at position 4, was expecting array delimiter but found \'&\'');
chai.assert.throws(() => decode.decode(toBytes('{"a":!}')), 'CBOR decode error: unexpected character at position 5');
chai.assert.throws(() => decode.decode(toBytes('"abc')), 'CBOR decode error: unexpected end of string at position 4');
chai.assert.throws(() => decode.decode(toBytes('"ab\\xc"')), 'CBOR decode error: unexpected string escape character at position 5');
chai.assert.throws(() => decode.decode(toBytes('"ab\x1Ec"')), 'CBOR decode error: invalid control character at position 3');
chai.assert.throws(() => decode.decode(toBytes('"ab\\')), 'CBOR decode error: unexpected string termination at position 4');
chai.assert.throws(() => decode.decode(toBytes('"\u263A').subarray(0, 3)), 'CBOR decode error: unexpected unicode sequence at position 1');
chai.assert.throws(() => decode.decode(toBytes('"\\uxyza"')), 'CBOR decode error: unexpected unicode escape character at position 3');
chai.assert.throws(() => decode.decode(toBytes('"\\u11"')), 'CBOR decode error: unexpected end of unicode escape sequence at position 3');
chai.assert.throws(() => decode.decode(toBytes('-boop')), 'CBOR decode error: unexpected token at position 1');
chai.assert.throws(() => decode.decode(toBytes('{"v":nope}')), 'CBOR decode error: unexpected token at position 7, expected to find \'null\'');
chai.assert.throws(() => decode.decode(toBytes('[n]')), 'CBOR decode error: unexpected end of input at position 1');
chai.assert.throws(() => decode.decode(toBytes('{"v":truu}')), 'CBOR decode error: unexpected token at position 9, expected to find \'true\'');
chai.assert.throws(() => decode.decode(toBytes('[tr]')), 'CBOR decode error: unexpected end of input at position 1');
chai.assert.throws(() => decode.decode(toBytes('{"v":flase}')), 'CBOR decode error: unexpected token at position 7, expected to find \'false\'');
chai.assert.throws(() => decode.decode(toBytes('[fa]')), 'CBOR decode error: unexpected end of input at position 1');
chai.assert.throws(() => decode.decode(toBytes('-0..1')), 'CBOR decode error: unexpected token at position 3');
});
it('should throw when rejectDuplicateMapKeys enabled on duplicate keys', () => {
chai.assert.deepStrictEqual(decode.decode(toBytes('{"foo":1,"foo":2}')), { foo: 2 });
chai.assert.throws(() => decode.decode(toBytes('{"foo":1,"foo":2}'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/);
});
});
+63
View File
@@ -0,0 +1,63 @@
'use strict';
var chai = require('chai');
var ipldGarbage = require('ipld-garbage');
var _0uint = require('../lib/0uint.js');
require('../cborg.js');
var length = require('../lib/length.js');
var common = require('./common.js');
var encode = require('../lib/encode.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chai__default = /*#__PURE__*/_interopDefaultLegacy(chai);
const {assert} = chai__default["default"];
function verifyLength(object, options) {
const len = length.encodedLength(object, options);
const encoded = encode.encode(object, options);
const actual = encoded.length;
assert.strictEqual(actual, len, JSON.stringify(object));
}
describe('encodedLength', () => {
it('int boundaries', () => {
for (let ii = 0; ii < 4; ii++) {
verifyLength(_0uint.uintBoundaries[ii]);
verifyLength(_0uint.uintBoundaries[ii] - 1);
verifyLength(_0uint.uintBoundaries[ii] + 1);
verifyLength(-1 * _0uint.uintBoundaries[ii]);
verifyLength(-1 * _0uint.uintBoundaries[ii] - 1);
verifyLength(-1 * _0uint.uintBoundaries[ii] + 1);
}
});
it('tags', () => {
verifyLength({ date: new Date('2013-03-21T20:04:00Z') }, { typeEncoders: { Date: common.dateEncoder } });
});
it('floats', () => {
verifyLength(0.5);
verifyLength(0.5, { float64: true });
verifyLength(8.940696716308594e-8);
verifyLength(8.940696716308594e-8, { float64: true });
});
it('small garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 1000; ii++) {
const gbg = ipldGarbage.garbage(1 << 6, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
it('medium garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 100; ii++) {
const gbg = ipldGarbage.garbage(1 << 16, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
it('large garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 10; ii++) {
const gbg = ipldGarbage.garbage(1 << 20, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
});
+44
View File
@@ -0,0 +1,44 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
require('./cborg.js');
var token = require('./lib/token.js');
const neg1b = BigInt(-1);
const pos1b = BigInt(1);
const zerob = BigInt(0);
const eightb = BigInt(8);
function bigIntDecoder(bytes) {
let bi = zerob;
for (let ii = 0; ii < bytes.length; ii++) {
bi = (bi << eightb) + BigInt(bytes[ii]);
}
return bi;
}
function fromBigInt(bi) {
const buf = [];
while (bi > 0) {
buf.unshift(Number(bi) & 255);
bi >>= eightb;
}
return Uint8Array.from(buf);
}
const maxSafeBigInt = BigInt('18446744073709551615');
const minSafeBigInt = BigInt('-18446744073709551616');
function bigIntEncoder(obj) {
if (obj >= minSafeBigInt && obj <= maxSafeBigInt) {
return null;
}
return [
new token.Token(token.Type.tag, obj >= zerob ? 2 : 3),
new token.Token(token.Type.bytes, fromBigInt(obj >= zerob ? obj : obj * neg1b - pos1b))
];
}
function bigNegIntDecoder(bytes) {
return neg1b - bigIntDecoder(bytes);
}
exports.bigIntDecoder = bigIntDecoder;
exports.bigIntEncoder = bigIntEncoder;
exports.bigNegIntDecoder = bigNegIntDecoder;
Generated Vendored Executable
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env node
// this exists because of limitations in ipjs, we copy it manually into
// the build and it holds our #!
// @ts-ignore
require('./cjs/lib/bin.js')
+640
View File
@@ -0,0 +1,640 @@
const fixtures = [
{
cbor: 'AA==',
hex: '00',
roundtrip: true,
decoded: 0
},
{
cbor: 'AQ==',
hex: '01',
roundtrip: true,
decoded: 1
},
{
cbor: 'Cg==',
hex: '0a',
roundtrip: true,
decoded: 10
},
{
cbor: 'Fw==',
hex: '17',
roundtrip: true,
decoded: 23
},
{
cbor: 'GBg=',
hex: '1818',
roundtrip: true,
decoded: 24
},
{
cbor: 'GBk=',
hex: '1819',
roundtrip: true,
decoded: 25
},
{
cbor: 'GGQ=',
hex: '1864',
roundtrip: true,
decoded: 100
},
{
cbor: 'GQPo',
hex: '1903e8',
roundtrip: true,
decoded: 1000
},
{
cbor: 'GgAPQkA=',
hex: '1a000f4240',
roundtrip: true,
decoded: 1000000
},
{
cbor: 'GwAAAOjUpRAA',
hex: '1b000000e8d4a51000',
roundtrip: true,
decoded: 1000000000000
},
{
cbor: 'G///////////',
hex: '1bffffffffffffffff',
roundtrip: true,
decoded: BigInt('18446744073709551615')
},
{
cbor: 'wkkBAAAAAAAAAAA=',
hex: 'c249010000000000000000',
roundtrip: true,
decoded: BigInt('18446744073709551616'),
noTagDecodeError: /tag not supported \(2\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'O///////////',
hex: '3bffffffffffffffff',
roundtrip: true,
decoded: BigInt('-18446744073709551616')
},
{
cbor: 'w0kBAAAAAAAAAAA=',
hex: 'c349010000000000000000',
roundtrip: true,
decoded: BigInt('-18446744073709551617'),
noTagDecodeError: /tag not supported \(3\)/,
noTagEncodeError: /BigInt larger than allowable range/
},
{
cbor: 'IA==',
hex: '20',
roundtrip: true,
decoded: -1
},
{
cbor: 'KQ==',
hex: '29',
roundtrip: true,
decoded: -10
},
{
cbor: 'OGM=',
hex: '3863',
roundtrip: true,
decoded: -100
},
{
cbor: 'OQPn',
hex: '3903e7',
roundtrip: true,
decoded: -1000
},
{
cbor: '+QAA',
hex: 'f90000',
roundtrip: false,
decoded: 0
},
{
cbor: '+YAA',
hex: 'f98000',
roundtrip: false,
decoded: -0
},
{
cbor: '+TwA',
hex: 'f93c00',
roundtrip: false,
decoded: 1
},
{
cbor: '+z/xmZmZmZma',
hex: 'fb3ff199999999999a',
roundtrip: true,
decoded: 1.1
},
{
cbor: '+T4A',
hex: 'f93e00',
roundtrip: true,
decoded: 1.5
},
{
cbor: '+Xv/',
hex: 'f97bff',
roundtrip: false,
decoded: 65504
},
{
cbor: '+kfDUAA=',
hex: 'fa47c35000',
roundtrip: false,
decoded: 100000
},
{
cbor: '+n9///8=',
hex: 'fa7f7fffff',
roundtrip: true,
decoded: 3.4028234663852886e+38
},
{
cbor: '+3435DyIAHWc',
hex: 'fb7e37e43c8800759c',
roundtrip: true,
decoded: 1e+300
},
{
cbor: '+QAB',
hex: 'f90001',
roundtrip: true,
decoded: 5.960464477539063e-8
},
{
cbor: '+QQA',
hex: 'f90400',
roundtrip: true,
decoded: 0.00006103515625
},
{
cbor: '+cQA',
hex: 'f9c400',
roundtrip: false,
decoded: -4
},
{
cbor: '+8AQZmZmZmZm',
hex: 'fbc010666666666666',
roundtrip: true,
decoded: -4.1
},
{
cbor: '+XwA',
hex: 'f97c00',
roundtrip: true,
diagnostic: Infinity
},
{
cbor: '+X4A',
hex: 'f97e00',
roundtrip: true,
diagnostic: NaN
},
{
cbor: '+fwA',
hex: 'f9fc00',
roundtrip: true,
diagnostic: -Infinity
},
{
cbor: '+n+AAAA=',
hex: 'fa7f800000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+n/AAAA=',
hex: 'fa7fc00000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+v+AAAA=',
hex: 'faff800000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '+3/wAAAAAAAA',
hex: 'fb7ff0000000000000',
roundtrip: false,
diagnostic: Infinity
},
{
cbor: '+3/4AAAAAAAA',
hex: 'fb7ff8000000000000',
roundtrip: false,
diagnostic: NaN
},
{
cbor: '+//wAAAAAAAA',
hex: 'fbfff0000000000000',
roundtrip: false,
diagnostic: -Infinity
},
{
cbor: '9A==',
hex: 'f4',
roundtrip: true,
decoded: false
},
{
cbor: '9Q==',
hex: 'f5',
roundtrip: true,
decoded: true
},
{
cbor: '9g==',
hex: 'f6',
roundtrip: true,
decoded: null
},
{
cbor: '9w==',
hex: 'f7',
roundtrip: true,
diagnostic: undefined
},
{
cbor: '8A==',
hex: 'f0',
roundtrip: true,
diagnostic: 'simple(16)',
error: /simple values are not supported/
},
{
cbor: '+Bg=',
hex: 'f818',
roundtrip: true,
diagnostic: 'simple(24)',
error: /simple values are not supported/
},
{
cbor: '+P8=',
hex: 'f8ff',
roundtrip: true,
diagnostic: 'simple(255)',
error: /simple values are not supported/
},
{
cbor: 'wHQyMDEzLTAzLTIxVDIwOjA0OjAwWg==',
hex: 'c074323031332d30332d32315432303a30343a30305a',
roundtrip: false,
diagnostic: '0("2013-03-21T20:04:00Z")'
},
{
cbor: 'wRpRS2ew',
hex: 'c11a514b67b0',
roundtrip: false,
diagnostic: '1(1363896240)'
},
{
cbor: 'wftB1FLZ7CAAAA==',
hex: 'c1fb41d452d9ec200000',
roundtrip: false,
diagnostic: '1(1363896240.5)'
},
{
cbor: '10QBAgME',
hex: 'd74401020304',
roundtrip: false,
diagnostic: '23(h\'01020304\')'
},
{
cbor: '2BhFZElFVEY=',
hex: 'd818456449455446',
roundtrip: false,
diagnostic: '24(h\'6449455446\')'
},
{
cbor: '2CB2aHR0cDovL3d3dy5leGFtcGxlLmNvbQ==',
hex: 'd82076687474703a2f2f7777772e6578616d706c652e636f6d',
roundtrip: false,
diagnostic: '32("http://www.example.com")'
},
{
cbor: 'QA==',
hex: '40',
roundtrip: true,
diagnostic: 'h\'\''
},
{
cbor: 'RAECAwQ=',
hex: '4401020304',
roundtrip: true,
diagnostic: 'h\'01020304\''
},
{
cbor: 'YA==',
hex: '60',
roundtrip: true,
decoded: ''
},
{
cbor: 'YWE=',
hex: '6161',
roundtrip: true,
decoded: 'a'
},
{
cbor: 'ZElFVEY=',
hex: '6449455446',
roundtrip: true,
decoded: 'IETF'
},
{
cbor: 'YiJc',
hex: '62225c',
roundtrip: true,
decoded: '"\\'
},
{
cbor: 'YsO8',
hex: '62c3bc',
roundtrip: true,
decoded: 'ü'
},
{
cbor: 'Y+awtA==',
hex: '63e6b0b4',
roundtrip: true,
decoded: '水'
},
{
cbor: 'ZPCQhZE=',
hex: '64f0908591',
roundtrip: true,
decoded: '\uD800\uDD51'
},
{
cbor: 'gA==',
hex: '80',
roundtrip: true,
decoded: []
},
{
cbor: 'gwECAw==',
hex: '83010203',
roundtrip: true,
decoded: [
1,
2,
3
]
},
{
cbor: 'gwGCAgOCBAU=',
hex: '8301820203820405',
roundtrip: true,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'mBkBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgYGBk=',
hex: '98190102030405060708090a0b0c0d0e0f101112131415161718181819',
roundtrip: true,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'oA==',
hex: 'a0',
roundtrip: true,
decoded: {}
},
{
cbor: 'ogECAwQ=',
hex: 'a201020304',
roundtrip: true,
diagnostic: '{1: 2, 3: 4}',
error: /non-string keys not supported/
},
{
cbor: 'omFhAWFiggID',
hex: 'a26161016162820203',
roundtrip: true,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhoWFiYWM=',
hex: '826161a161626163',
roundtrip: true,
decoded: [
'a',
{ b: 'c' }
]
},
{
cbor: 'pWFhYUFhYmFCYWNhQ2FkYURhZWFF',
hex: 'a56161614161626142616361436164614461656145',
roundtrip: true,
decoded: {
a: 'A',
b: 'B',
c: 'C',
d: 'D',
e: 'E'
}
},
{
cbor: 'X0IBAkMDBAX/',
hex: '5f42010243030405ff',
roundtrip: false,
diagnostic: '(_ h\'0102\', h\'030405\')',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'f2VzdHJlYWRtaW5n/w==',
hex: '7f657374726561646d696e67ff',
roundtrip: false,
decoded: 'streaming',
error: /indefinite length bytes\/strings are not supported/
},
{
cbor: 'n/8=',
hex: '9fff',
roundtrip: false,
decoded: []
},
{
cbor: 'nwGCAgOfBAX//w==',
hex: '9f018202039f0405ffff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwGCAgOCBAX/',
hex: '9f01820203820405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGCAgOfBAX/',
hex: '83018202039f0405ff',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'gwGfAgP/ggQF',
hex: '83019f0203ff820405',
roundtrip: false,
decoded: [
1,
[
2,
3
],
[
4,
5
]
]
},
{
cbor: 'nwECAwQFBgcICQoLDA0ODxAREhMUFRYXGBgYGf8=',
hex: '9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff',
roundtrip: false,
decoded: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
]
},
{
cbor: 'v2FhAWFinwID//8=',
hex: 'bf61610161629f0203ffff',
roundtrip: false,
decoded: {
a: 1,
b: [
2,
3
]
}
},
{
cbor: 'gmFhv2FiYWP/',
hex: '826161bf61626163ff',
roundtrip: false,
decoded: [
'a',
{ b: 'c' }
]
},
{
cbor: 'v2NGdW71Y0FtdCH/',
hex: 'bf6346756ef563416d7421ff',
roundtrip: false,
decoded: {
Fun: true,
Amt: -2
}
}
];
export {
fixtures
};
+19
View File
@@ -0,0 +1,19 @@
import {
Token,
Type
} from '../lib/token.js';
export function dateDecoder(obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1');
}
return new Date(obj);
}
export function dateEncoder(obj) {
if (!(obj instanceof Date)) {
throw new Error('expected Date for "Date" encoder');
}
return [
new Token(Type.tag, 0),
new Token(Type.string, obj.toISOString().replace(/\.000Z$/, 'Z'))
];
}
+339
View File
@@ -0,0 +1,339 @@
import chai from 'chai';
import { exec } from 'child_process';
import process from 'process';
import path from 'path';
import { platform } from 'os';
import { fileURLToPath } from 'url';
const {assert} = chai;
const fixture1JsonString = '{"a":1,"b":[2,3],"smile":"\uD83D\uDE00"}';
const fixture1JsonPrettyString = `{
"a": 1,
"b": [
2,
3
],
"smile": "😀"
}
`;
const fixture1HexString = 'a3616101616282020365736d696c6564f09f9880';
const fixture1Bin = fromHex(fixture1HexString);
const fixture1BinString = new TextDecoder().decode(fixture1Bin);
const fixture1DiagnosticString = `a3 # map(3)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`;
const fixture2HexString = 'a4616101616282020363627566440102036165736d696c6564f09f9880';
const fixture2DiagnosticString = `a4 # map(4)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
82 # array(2)
02 # uint(2)
03 # uint(3)
63 # string(3)
627566 # "buf"
44 # bytes(4)
01020361 # "\\x01\\x02\\x03a"
65 # string(5)
736d696c65 # "smile"
64 # string(2)
f09f9880 # "😀"
`;
const binPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../lib/bin.js');
function fromHex(hex) {
return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
}
async function execBin(cmd, stdin) {
return new Promise((resolve, reject) => {
const cp = exec(`"${ process.execPath }" "${ binPath }" ${ cmd }`, (err, stdout, stderr) => {
if (err) {
err.stdout = stdout;
err.stderr = stderr;
return reject(err);
}
resolve({
stdout,
stderr
});
});
if (stdin != null) {
cp.on('spawn', () => {
cp.stdin.write(stdin);
cp.stdin.end();
});
}
});
}
describe('Bin', () => {
it('usage', async () => {
try {
await execBin('');
assert.fail('should have errored');
} catch (e) {
assert.strictEqual(e.stdout, '');
assert.strictEqual(e.stderr, `Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
}
});
it('bad cmd', async () => {
try {
await execBin('blip');
assert.fail('should have errored');
} catch (e) {
assert.strictEqual(e.stdout, '');
assert.strictEqual(e.stderr, `Unknown command: 'blip'
Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
}
});
it('help', async () => {
const {stdout, stderr} = await execBin('help');
assert.strictEqual(stdout, '');
assert.strictEqual(stderr, `Usage: cborg <command> <args>
Valid commands:
\tbin2diag [binary input]
\tbin2hex [binary input]
\tbin2json [--pretty] [binary input]
\tdiag2bin [diagnostic input]
\tdiag2hex [diagnostic input]
\tdiag2json [--pretty] [diagnostic input]
\thex2bin [hex input]
\thex2diag [hex input]
\thex2json [--pretty] [hex input]
\tjson2bin '[json input]'
\tjson2diag '[json input]'
\tjson2hex '[json input]'
Input may either be supplied as an argument or piped via stdin
`);
});
it('bin2diag (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2diag', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1DiagnosticString);
});
it('bin2hex (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2hex', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
it('bin2json (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2json', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it('bin2json pretty (stdin)', async () => {
const {stdout, stderr} = await execBin('bin2json --pretty', fixture1Bin);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
for (const stdin of [
true,
false
]) {
if (platform() !== 'win32' || stdin) {
it(`diag2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2bin '${ fixture1DiagnosticString }'`) : await execBin('diag2bin', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`diag2hex${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2hex '${ fixture1DiagnosticString }'`) : await execBin('diag2hex', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
it(`diag2json${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2json '${ fixture1DiagnosticString }'`) : await execBin('diag2json', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it(`diag2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`diag2json --pretty '${ fixture1DiagnosticString }'`) : await execBin('diag2json --pretty', fixture1DiagnosticString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
}
it(`hex2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2bin ${ fixture1HexString }`) : await execBin('hex2bin', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`hex2diag${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2diag ${ fixture2HexString }`) : await execBin('hex2diag', fixture2HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture2DiagnosticString);
});
it(`hex2json${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2json ${ fixture1HexString }`) : await execBin('hex2json', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1JsonString }\n`);
});
it(`hex2json pretty${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`hex2json --pretty ${ fixture1HexString }`) : await execBin('hex2json --pretty', fixture1HexString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1JsonPrettyString);
});
it(`json2bin${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2bin', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1BinString);
});
it(`json2diag${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"\uD83D\uDE00\\"}"') : await execBin('json2diag', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, fixture1DiagnosticString);
});
it(`json2hex${ stdin ? ' (stdin)' : '' }`, async () => {
const {stdout, stderr} = !stdin ? await execBin(`json2hex "${ fixture1JsonString.replace(/"/g, '\\"') }"`) : await execBin('json2hex', fixture1JsonString);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ fixture1HexString }\n`);
});
}
it('diag indenting', async () => {
const {stdout, stderr} = await execBin('json2diag', '{"a":[],"b":{},"c":{"a":1,"b":{"a":{"a":{}}}},"d":{"a":{"a":{"a":1},"b":2,"c":[]}},"e":[[[[{"a":{}}]]]],"f":1}');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `a6 # map(6)
61 # string(1)
61 # "a"
80 # array(0)
61 # string(1)
62 # "b"
a0 # map(0)
61 # string(1)
63 # "c"
a2 # map(2)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
a1 # map(1)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
64 # "d"
a1 # map(1)
61 # string(1)
61 # "a"
a3 # map(3)
61 # string(1)
61 # "a"
a1 # map(1)
61 # string(1)
61 # "a"
01 # uint(1)
61 # string(1)
62 # "b"
02 # uint(2)
61 # string(1)
63 # "c"
80 # array(0)
61 # string(1)
65 # "e"
81 # array(1)
81 # array(1)
81 # array(1)
81 # array(1)
a1 # map(1)
61 # string(1)
61 # "a"
a0 # map(0)
61 # string(1)
66 # "f"
01 # uint(1)
`);
});
describe('diag length bytes', () => {
it('compact', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `77 # string(23)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
`);
});
it('1-byte', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `78 23 # string(35)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161616161616161616161 # "aaaaaaaaaaaa"
`);
});
it('2-byte', async () => {
const {stdout, stderr} = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"');
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `79 0100 # string(256)
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa"
616161 # "aaa"
`);
});
});
it('diag non-utf8 and non-printable ascii', async () => {
const input = '7864f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c11e756338bd93865e645f1adec9b9c99ef407fbd4fc6859e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82f9f18c3d03418e35';
let {stdout, stderr} = await execBin(`hex2diag ${ input }`);
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `78 64 # string(86)
f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b # "õ_øñ%\\x08¶>ò¿ì§Uzé\\x0dö1\\x1a^Ác\\x1b"
4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c # "J\\x1f¨C1\\x0bÙç\\x10ê¬å¡½×*пàIw\\x1c"
11e756338bd93865e645f1adec9b9c99ef407fbd4fc685 # "\\x11çV3\\x8bÙ8eæEñ\\xadì\\x9b\\x9c\\x99ï@\\x7f½OÆ\\x85"
9e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82 # "\\x9ey\\x04Å\\xad}ɽ\\x10¥Ì\\x16\\x97=[(ì\\x1amÔ=\\x9f\\x82"
f9f18c3d03418e35 # "ùñ\\x8c=\\x03A\\x8e5"
`);
({stdout, stderr} = await execBin('diag2hex', stdout));
assert.strictEqual(stderr, '');
assert.strictEqual(stdout, `${ input }\n`);
});
});
+1
View File
@@ -0,0 +1 @@
import bin from '../lib/bin.js';
+155
View File
@@ -0,0 +1,155 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: '00',
expected: 0,
type: 'uint8'
},
{
data: '02',
expected: 2,
type: 'uint8'
},
{
data: '18ff',
expected: 255,
type: 'uint8'
},
{
data: '1901f4',
expected: 500,
type: 'uint16'
},
{
data: '1900ff',
expected: 255,
type: 'uint16',
strict: false
},
{
data: '19ffff',
expected: 65535,
type: 'uint16'
},
{
data: '1a000000ff',
expected: 255,
type: 'uint32',
strict: false
},
{
data: '1a00010000',
expected: 65536,
type: 'uint32'
},
{
data: '1a000f4240',
expected: 1000000,
type: 'uint32'
},
{
data: '1aa5f702b3',
expected: 2784428723,
type: 'uint32'
},
{
data: '1b00000000000000ff',
expected: 255,
type: 'uint64',
strict: false
},
{
data: '1b0016db6db6db6db7',
expected: Number.MAX_SAFE_INTEGER / 1.4,
type: 'uint64'
},
{
data: '1b001fffffffffffff',
expected: Number.MAX_SAFE_INTEGER,
type: 'uint64'
},
{
data: '1ba5f702b3a5f702b3',
expected: BigInt('11959030306112471731'),
type: 'uint64'
},
{
data: '1bffffffffffffffff',
expected: BigInt('18446744073709551615'),
type: 'uint64'
}
];
describe('uint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode(data) === fixture.expected, `decode ${ fixture.type } ${ decode(data) } != ${ fixture.expected }`);
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.ok(decode(data, { strict: true }) === fixture.expected, `decode ${ fixture.type }`);
}
});
}
});
it('should throw error', () => {
assert.throws(() => decode(fromHex('1ca5f702b3a5f702b3')), Error, 'CBOR decode error: encountered invalid minor (28) for major 0');
assert.throws(() => decode(fromHex('1ba5f702b3a5f702')), Error, 'CBOR decode error: not enough data for type');
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode(encode(fixture.expected)) === fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('toobig', () => {
it('bigger than 64-bit', () => {
assert.doesNotThrow(() => encode(BigInt('18446744073709551615')));
assert.throws(() => encode(BigInt('18446744073709551616')), /BigInt larger than allowable range/);
});
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode(data, { allowBigInt: false }), /safe integer range/);
} else {
assert.ok(decode(data, { allowBigInt: false }) === fixture.expected, `decode ${ fixture.type }`);
}
}
});
});
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected);
it(`should encode ${ small }n`, () => {
assert.strictEqual(toHex(encode(BigInt(small))), fixture.data, `encode ${ small }`);
});
}
}
});
});
+149
View File
@@ -0,0 +1,149 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: '20',
expected: -1,
type: 'negint8'
},
{
data: '22',
expected: -3,
type: 'negint8'
},
{
data: '3863',
expected: -100,
type: 'negint8'
},
{
data: '38ff',
expected: -256,
type: 'negint8'
},
{
data: '3900ff',
expected: -256,
type: 'negint16',
strict: false
},
{
data: '3901f4',
expected: -501,
type: 'negint16'
},
{
data: '3a000000ff',
expected: -256,
type: 'negint32',
strict: false
},
{
data: '3aa5f702b3',
expected: -2784428724,
type: 'negint32'
},
{
data: '3b00000000000000ff',
expected: -256,
type: 'negint32',
strict: false
},
{
data: '3b0016db6db6db6db7',
expected: Number.MIN_SAFE_INTEGER / 1.4 - 1,
type: 'negint64'
},
{
data: '3b001ffffffffffffe',
expected: Number.MIN_SAFE_INTEGER,
type: 'negint64'
},
{
data: '3b001fffffffffffff',
expected: BigInt('-9007199254740992'),
type: 'negint64'
},
{
data: '3b0020000000000000',
expected: BigInt('-9007199254740993'),
type: 'negint64'
},
{
data: '3ba5f702b3a5f702b3',
expected: BigInt('-11959030306112471732'),
type: 'negint64'
},
{
data: '3bffffffffffffffff',
expected: BigInt('-18446744073709551616'),
type: 'negint64'
}
];
describe('negint', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode(data) === fixture.expected, `decode ${ fixture.type } (${ decode(data) } != ${ fixture.expected })`);
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.strictEqual(decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
}
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.ok(decode(encode(fixture.expected)) === fixture.expected, `roundtrip ${ fixture.type }`);
});
}
});
describe('toobig', () => {
it('bigger than 64-bit', () => {
assert.doesNotThrow(() => encode(BigInt('-18446744073709551616')));
assert.throws(() => encode(BigInt('-18446744073709551617')), /BigInt larger than allowable range/);
});
it('disallow BigInt', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
if (!Number.isSafeInteger(fixture.expected)) {
assert.throws(() => decode(data, { allowBigInt: false }), /safe integer range/);
} else {
assert.ok(decode(data, { allowBigInt: false }) === fixture.expected, `decode ${ fixture.type }`);
}
}
});
});
describe('toosmall', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false && typeof fixture.expected === 'number') {
const small = BigInt(fixture.expected);
it(`should encode ${ small }n`, () => {
assert.strictEqual(toHex(encode(BigInt(small))), fixture.data, `encode ${ small }`);
});
}
}
});
});
+252
View File
@@ -0,0 +1,252 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
useBuffer,
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: '40',
expected: '',
type: 'bytes'
},
{
data: '41a1',
expected: 'a1',
type: 'bytes'
},
{
data: '5801a1',
expected: 'a1',
type: 'bytes',
strict: false
},
{
data: '58ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 8-bit length'
},
{
data: '5900ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 16-bit length',
strict: false
},
{
data: '5a000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 32-bit length',
strict: false
},
{
data: '5b00000000000000ff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
expected: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe',
type: 'bytes',
label: 'long bytes, 64-bit length',
strict: false
}
];
(() => {
function rnd(length) {
return new Uint8Array(Array.from({ length }, () => Math.floor(Math.random() * 255)));
}
const expected16 = rnd(256);
fixtures.push({
data: new Uint8Array([
...fromHex('590100'),
...expected16
]),
expected: expected16,
type: 'bytes',
label: 'long bytes, 16-bit length strict-compat'
});
const expected32 = rnd(65536);
fixtures.push({
data: new Uint8Array([
...fromHex('5a00010000'),
...expected32
]),
expected: expected32,
type: 'bytes',
label: 'long bytes, 32-bit length strict-compat'
});
})();
describe('bytes', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
let actual = decode(data);
assert.strictEqual(toHex(actual), toHex(fromHex(fixture.expected)), `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
actual = decode(data, { strict: true });
assert.strictEqual(toHex(actual), toHex(fromHex(fixture.expected)), `decode ${ fixture.type } strict`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode(fromHex('5ba5f702b3a5f702b3000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe')), /CBOR decode error: 64-bit integer bytes lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large bytes ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
});
continue;
}
const data = fromHex(fixture.expected);
const expectedHex = toHex(fixture.data);
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(() => encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/);
} else if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(data)), expectedHex, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(toHex(encode(data)), expectedHex, `encode ${ fixture.type }`);
}
});
}
});
describe('typedarrays', () => {
const cases = [
{
obj: Uint8Array.from([
1,
2,
3
]),
hex: '43010203'
},
{
obj: Uint8ClampedArray.from([
1,
2,
3
]),
hex: '43010203'
},
{
obj: Uint16Array.from([
1,
2,
3
]),
hex: '46010002000300'
},
{
obj: Uint32Array.from([
1,
2,
3
]),
hex: '4c010000000200000003000000'
},
{
obj: Int8Array.from([
1,
2,
-3
]),
hex: '430102fd'
},
{
obj: Int16Array.from([
1,
2,
-3
]),
hex: '4601000200fdff'
},
{
obj: Int32Array.from([
1,
2,
-3
]),
hex: '4c0100000002000000fdffffff'
},
{
obj: Float32Array.from([
1,
2,
-3
]),
hex: '4c0000803f00000040000040c0'
},
{
obj: Float64Array.from([
1,
2,
-3
]),
hex: '5818000000000000f03f000000000000004000000000000008c0'
},
{
obj: BigUint64Array.from([
BigInt(1),
BigInt(2),
BigInt(3)
]),
hex: '5818010000000000000002000000000000000300000000000000'
},
{
obj: BigInt64Array.from([
BigInt(1),
BigInt(2),
BigInt(-3)
]),
hex: '581801000000000000000200000000000000fdffffffffffffff'
},
{
obj: new DataView(Uint8Array.from([
1,
2,
3
]).buffer),
hex: '43010203'
},
{
obj: Uint8Array.from([
1,
2,
3
]).buffer,
hex: '43010203'
}
];
for (const testCase of cases) {
it(testCase.obj.constructor.name, () => {
assert.equal(toHex(encode(testCase.obj)), testCase.hex);
const decoded = decode(fromHex(testCase.hex));
assert.instanceOf(decoded, Uint8Array);
assert.equal(toHex(decoded), toHex(testCase.obj));
});
}
});
if (useBuffer) {
describe('buffer', () => {
it('can encode Node.js Buffers', () => {
const obj = global.Buffer.from([
1,
2,
3
]);
assert.equal(toHex(encode(obj)), '43010203');
const decoded = decode(fromHex('43010203'));
assert.instanceOf(decoded, Uint8Array);
assert.equal(toHex(decoded), toHex(obj));
});
});
}
});
+141
View File
@@ -0,0 +1,141 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: '60',
expected: '',
type: 'string'
},
{
data: '6161',
expected: 'a',
type: 'string'
},
{
data: '780161',
expected: 'a',
type: 'string',
strict: false
},
{
data: '6c48656c6c6f20776f726c6421',
expected: 'Hello world!',
type: 'string'
},
{
data: '6fc48c6175657320c39f76c49b746521',
expected: 'Čaues ßvěte!',
type: 'string'
},
{
data: '78964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 8-bit length'
},
{
data: '7900964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 16-bit length',
strict: false
},
{
data: '7a000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 32-bit length',
strict: false
},
{
data: '7b00000000000000964c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e',
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.',
type: 'string',
label: 'long string, 64-bit length',
strict: false
}
];
(() => {
function rnd(length) {
const sa = [];
let l = 0;
while (l < length) {
const ascii = length - l < 3;
const base = ascii ? 32 : 126976;
const max = ascii ? 126 : 130816;
const cc = Math.floor(Math.random() * (max - base)) + base;
const s = String.fromCharCode(cc);
l += new TextEncoder().encode(s).length;
sa.push(s);
}
return sa.join('');
}
const expected16 = rnd(256);
fixtures.push({
data: new Uint8Array([
...fromHex('790100'),
...new TextEncoder().encode(expected16)
]),
expected: expected16,
type: 'string',
label: 'long string, 16-bit length strict-compat'
});
const expected32 = rnd(65536);
fixtures.push({
data: new Uint8Array([
...fromHex('7a00010000'),
...new TextEncoder().encode(expected32)
]),
expected: expected32,
type: 'string',
label: 'long string, 32-bit length strict-compat'
});
})();
describe('string', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
let actual = decode(data);
assert.strictEqual(actual, fixture.expected, `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
actual = decode(data, { strict: true });
assert.strictEqual(actual, fixture.expected, `decode ${ fixture.type } strict`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode(fromHex('7ba5f702b3a5f702b34c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e')), /CBOR decode error: 64-bit integer string lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.data.length >= 100000000) {
it.skip(`(TODO) skipping encode of very large string ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
});
continue;
}
const data = fixture.expected;
const expectedHex = toHex(fixture.data);
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(() => encode(data), Error, /^CBOR encode error: number too large to encode \(-\d+\)$/);
} else if (fixture.strict === false) {
assert.notStrictEqual(toHex(encode(data)), expectedHex, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(toHex(encode(data)), expectedHex, `encode ${ fixture.type }`);
}
});
}
});
});
+197
View File
@@ -0,0 +1,197 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: '80',
expected: [],
type: 'array empty'
},
{
data: '8102',
expected: [2],
type: 'array 1 compact uint'
},
{
data: '8118ff',
expected: [255],
type: 'array 1 uint8'
},
{
data: '811901f4',
expected: [500],
type: 'array 1 uint16'
},
{
data: '811a00010000',
expected: [65536],
type: 'array 1 uint32'
},
{
data: '811b00000000000000ff',
expected: [255],
type: 'array 1 uint64',
strict: false
},
{
data: '811b0016db6db6db6db7',
expected: [Number.MAX_SAFE_INTEGER / 1.4],
type: 'array 1 uint64'
},
{
data: '811b001fffffffffffff',
expected: [Number.MAX_SAFE_INTEGER],
type: 'array 1 uint64'
},
{
data: '8403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints'
},
{
data: '8c1b0016db6db6db6db71a000100001901f40200202238ff3aa5f702b33b0016db6db6db6db74261316fc48c6175657320c39f76c49b746521',
expected: [
Number.MAX_SAFE_INTEGER / 1.4,
65536,
500,
2,
0,
-1,
-3,
-256,
-2784428724,
Number.MIN_SAFE_INTEGER / 1.4 - 1,
new TextEncoder().encode('a1'),
'Čaues ßvěte!'
],
type: 'array mixed terminals',
label: '[]'
},
{
data: '8265617272617982626f66820582666e657374656482666172726179736121',
expected: [
'array',
[
'of',
[
5,
[
'nested',
[
'arrays',
'!'
]
]
]
]
],
type: 'array nested'
},
{
data: '980403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length8',
strict: false
},
{
data: '99000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length16',
strict: false
},
{
data: '9a0000000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length32',
strict: false
},
{
data: '9b000000000000000403040506',
expected: [
3,
4,
5,
6
],
type: 'array 4 ints, length64',
strict: false
}
];
describe('array', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
assert.deepStrictEqual(decode(data), fixture.expected, `decode ${ fixture.type }`);
if (fixture.strict === false) {
assert.throws(() => decode(data, { strict: true }), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.deepStrictEqual(decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode(fromHex('9ba5f702b3a5f7020403040506')), /CBOR decode error: 64-bit integer array lengths not supported/);
});
}
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
if (fixture.unsafe) {
assert.throws(encode.bind(null, fixture.expected), Error, /^CBOR encode error: number too large to encode \(\d+\)$/);
} else if (fixture.strict === false) {
assert.notDeepEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.label || fixture.expected }`, () => {
assert.deepStrictEqual(decode(encode(fixture.expected)), fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode(fromHex('9f616f6174ff')), [
'o',
't'
]);
});
it('can switch off indefinite length support', () => {
assert.throws(() => decode(fromHex('9f616f6174ff'), { allowIndefinite: false }), /indefinite/);
});
});
});
+664
View File
@@ -0,0 +1,664 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: 'a0',
expected: {},
type: 'map empty'
},
{
data: 'a0',
expected: new Map(),
type: 'map empty (useMaps)',
useMaps: true
},
{
data: 'a1616101',
expected: { a: 1 },
type: 'map 1 pair'
},
{
data: 'a161316161',
expected: { 1: 'a' },
type: 'map 1 pair (rev)'
},
{
data: 'a1016161',
expected: toMap([[
1,
'a'
]]),
type: 'map 1 pair (int key as Map w/ useMaps)',
useMaps: true
},
{
data: 'a243010203633132334302030463323334',
expected: toMap([
[
Uint8Array.from([
1,
2,
3
]),
'123'
],
[
Uint8Array.from([
2,
3,
4
]),
'234'
]
]),
type: 'map 2 pair (bytes keys Map w/ useMaps)',
useMaps: true
},
{
data: 'a1666f626a656374a16477697468a26134666e6573746564676f626a65637473a161216121',
expected: {
object: {
with: {
4: 'nested',
objects: { '!': '!' }
}
}
},
type: 'map nested'
},
{
data: 'a1666f626a656374a16477697468a204666e6573746564676f626a65637473a161216121',
expected: toMap([[
'object',
toMap([[
'with',
toMap([
[
4,
'nested'
],
[
'objects',
toMap([[
'!',
'!'
]])
]
])
]])
]]),
type: 'map nested w/ useMaps',
useMaps: true
},
{
data: 'ae636f6e651b0016db6db6db6db763736978206374656e3b0016db6db6db6db76374776f1a0001000064666976650064666f757202646e696e653aa5f702b365656967687438ff65736576656e226574687265651901f466656c6576656e426131667477656c76656fc48c6175657320c39f76c49b74652168666f75727465656ea4616664666f7572616f016174026274680368746869727465656e840203046466697665',
encode: {
one: Number.MAX_SAFE_INTEGER / 1.4,
two: 65536,
three: 500,
four: 2,
five: 0,
six: -1,
seven: -3,
eight: -256,
nine: -2784428724,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
thirteen: [
2,
3,
4,
'five'
],
fourteen: {
o: 1,
t: 2,
th: 3,
f: 'four'
}
},
expected: {
one: Number.MAX_SAFE_INTEGER / 1.4,
six: -1,
ten: Number.MIN_SAFE_INTEGER / 1.4 - 1,
two: 65536,
five: 0,
four: 2,
nine: -2784428724,
eight: -256,
seven: -3,
three: 500,
eleven: new TextEncoder().encode('a1'),
twelve: 'Čaues ßvěte!',
fourteen: {
f: 'four',
o: 1,
t: 2,
th: 3
},
thirteen: [
2,
3,
4,
'five'
]
},
type: 'map with complex entries',
label: '{}'
},
{
data: 'ad01636f6e65026374776f1901f46c666976652068756e647265641902586b7369782068756e647265641a00010000636269671b0016db6db6db6db76662696767657220696d696e7573206f6e6521696d696e75732074776f38ff781f6d696e75732074776f2068756e6472656420616e64206669667479207369783901f4781a6d696e757820666976652068756e6472656420616e64206f6e653901f5781a6d696e757820666976652068756e6472656420616e642074776f3aa5f702b367626967206e65673b0016db6db6db6db76a626967676572206e6567',
encode: toMap([
[
2,
'two'
],
[
1,
'one'
],
[
-2,
'minus two'
],
[
-1,
'minus one'
],
[
600,
'six hundred'
],
[
500,
'five hundred'
],
[
-256,
'minus two hundred and fifty six'
],
[
-502,
'minux five hundred and two'
],
[
-501,
'minux five hundred and one'
],
[
65536,
'big'
],
[
-2784428724,
'big neg'
],
[
6433713753386423,
'bigger'
],
[
-6433713753386424,
'bigger neg'
]
]),
expected: toMap([
[
1,
'one'
],
[
2,
'two'
],
[
500,
'five hundred'
],
[
600,
'six hundred'
],
[
65536,
'big'
],
[
6433713753386423,
'bigger'
],
[
-1,
'minus one'
],
[
-2,
'minus two'
],
[
-256,
'minus two hundred and fifty six'
],
[
-501,
'minux five hundred and one'
],
[
-502,
'minux five hundred and two'
],
[
-2784428724,
'big neg'
],
[
-6433713753386424,
'bigger neg'
]
]),
type: 'map with ints and negints',
useMaps: true
},
{
data: 'a44104636f6e65430102026374776f430102036574687265654301020464666f7572',
encode: toMap([
[
Uint8Array.from([
1,
2,
3
]),
'three'
],
[
Uint8Array.from([4]),
'one'
],
[
Uint8Array.from([
1,
2,
4
]),
'four'
],
[
Uint8Array.from([
1,
2,
2
]),
'two'
]
]),
expected: toMap([
[
Uint8Array.from([4]),
'one'
],
[
Uint8Array.from([
1,
2,
2
]),
'two'
],
[
Uint8Array.from([
1,
2,
3
]),
'three'
],
[
Uint8Array.from([
1,
2,
4
]),
'four'
]
]),
type: 'map with bytes keys',
useMaps: true
},
{
data: 'b801616101',
expected: { a: 1 },
type: 'map 1 pair, length8',
strict: false
},
{
data: 'b90001616101',
expected: { a: 1 },
type: 'map 1 pair, length16',
strict: false
},
{
data: 'ba00000001616101',
expected: { a: 1 },
type: 'map 1 pair, length32',
strict: false
},
{
data: 'bb0000000000000001616101',
expected: { a: 1 },
type: 'map 1 pair, length64',
strict: false
}
];
function toMap(arr) {
const m = new Map();
for (const [key, value] of arr) {
m.set(key, value);
}
return m;
}
function entries(map) {
function nest(a) {
for (const e of a) {
e[0] = entries(e[0]);
e[1] = entries(e[1]);
}
return a;
}
if (Object.getPrototypeOf(map) === Map.prototype) {
return nest([...map.entries()]);
}
if (typeof map === 'object') {
return nest([...Object.entries(map)]);
}
return map;
}
describe('map', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
let options = fixture.useMaps ? { useMaps: true } : undefined;
const decoded = decode(data, options);
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(decoded), Map.prototype, 'is Map');
} else {
assert.isObject(decoded, 'is object');
}
assert.deepStrictEqual(entries(decoded), entries(fixture.expected), `decode ${ fixture.type }`);
options = Object.assign({ strict: true }, options);
if (fixture.strict === false) {
assert.throws(() => decode(data, options), Error, 'CBOR decode error: integer encoded in more bytes than necessary (strict decode)');
} else {
assert.deepStrictEqual(entries(decode(data, options)), entries(fixture.expected), `decode ${ fixture.type }`);
}
});
it('should fail to decode very large length', () => {
assert.throws(() => decode(fromHex('bba5f702b3a5f70201616101')), /CBOR decode error: 64-bit integer map lengths not supported/);
});
}
it('errors', () => {
assert.throws(() => decode(fromHex('a1016161')), /non-string keys not supported \(got number\)/);
});
});
describe('encode', () => {
for (const fixture of fixtures) {
it(`should encode ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
const toEncode = fixture.encode || fixture.expected;
if (fixture.unsafe) {
assert.throws(encode.bind(null, toEncode), Error, /^CBOR encode error: number too large to encode \(\d+\)$/);
} else if (fixture.strict === false || fixture.roundtrip === false) {
assert.notDeepEqual(toHex(encode(toEncode)), fixture.data, `encode ${ fixture.type } !strict`);
} else {
assert.strictEqual(toHex(encode(toEncode)), fixture.data, `encode ${ fixture.type }`);
}
});
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false && fixture.roundtrip !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.label || JSON.stringify(fixture.expected) }`, () => {
const toEncode = fixture.encode || fixture.expected;
const options = fixture.useMaps ? { useMaps: true } : undefined;
const rt = decode(encode(toEncode), options);
if (fixture.useMaps) {
assert.strictEqual(Object.getPrototypeOf(rt), Map.prototype, 'is Map');
} else {
assert.isObject(rt, 'is object');
}
assert.deepStrictEqual(entries(rt), entries(fixture.expected), `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('can decode indefinite length items', () => {
assert.deepStrictEqual(decode(fromHex('bf616f01617402ff')), {
o: 1,
t: 2
});
});
it('can switch off indefinite length support', () => {
assert.throws(() => decode(fromHex('bf616f01617402ff'), { allowIndefinite: false }), /indefinite/);
});
});
describe('sorting', () => {
it('sorts int map keys', () => {
assert.strictEqual(toHex(encode(new Map([
[
1,
1
],
[
2,
2
]
]))), 'a201010202');
assert.strictEqual(toHex(encode(new Map([
[
2,
1
],
[
1,
2
]
]))), 'a201020201');
});
it('sorts negint map keys', () => {
assert.strictEqual(toHex(encode(new Map([
[
-1,
1
],
[
-2,
2
]
]))), 'a220012102');
assert.strictEqual(toHex(encode(new Map([
[
-2,
1
],
[
-1,
2
]
]))), 'a220022101');
});
it('sorts bytes map keys', () => {
assert.strictEqual(toHex(encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
]
]))), 'a24201020142020102');
assert.strictEqual(toHex(encode(new Map([
[
Uint8Array.from([
2,
1
]),
1
],
[
Uint8Array.from([
1,
2
]),
2
]
]))), 'a24201020242020101');
assert.strictEqual(toHex(encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
],
[
Uint8Array.from([200]),
3
]
]))), 'a341c8034201020142020102');
});
it('sorts bytes map keys', () => {
assert.strictEqual(toHex(encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
]
]))), 'a24201020142020102');
assert.strictEqual(toHex(encode(new Map([
[
Uint8Array.from([
2,
1
]),
1
],
[
Uint8Array.from([
1,
2
]),
2
]
]))), 'a24201020242020101');
assert.strictEqual(toHex(encode(new Map([
[
Uint8Array.from([
1,
2
]),
1
],
[
Uint8Array.from([
2,
1
]),
2
],
[
Uint8Array.from([200]),
3
]
]))), 'a341c8034201020142020102');
});
it('sorts array map keys (length only)', () => {
assert.strictEqual(toHex(encode(new Map([
[
[1],
1
],
[
[
1,
1
],
2
]
]))), 'a281010182010102');
assert.strictEqual(toHex(encode(new Map([
[
[
1,
1
],
1
],
[
[1],
2
]
]))), 'a281010282010101');
});
it('sorts map map keys (length only)', () => {
assert.strictEqual(toHex(encode(new Map([
[
{ a: 1 },
1
],
[
{
a: 1,
b: 1
},
2
]
]))), 'a2a161610101a261610161620102');
assert.strictEqual(toHex(encode(new Map([
[
{
a: 1,
b: 1
},
1
],
[
{ a: 1 },
2
]
]))), 'a2a161610102a261610161620101');
});
});
});
+78
View File
@@ -0,0 +1,78 @@
import chai from 'chai';
import {
Token,
Type
} from '../lib/token.js';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
import {
dateDecoder,
dateEncoder
} from './common.js';
const {assert} = chai;
function Uint16ArrayDecoder(obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 23');
}
const u8a = fromHex(obj);
return new Uint16Array(u8a.buffer, u8a.byteOffset, u8a.length / 2);
}
function Uint16ArrayEncoder(obj) {
if (!(obj instanceof Uint16Array)) {
throw new Error('expected Uint16Array for "Uint16Array" encoder');
}
return [
new Token(Type.tag, 23),
new Token(Type.string, toHex(obj))
];
}
describe('tag', () => {
it('date', () => {
assert.throws(() => encode({ d: new Date() }), /unsupported type: Date/);
assert.equal(toHex(encode(new Date('2013-03-21T20:04:00Z'), { typeEncoders: { Date: dateEncoder } })), 'c074323031332d30332d32315432303a30343a30305a');
const decodedDate = decode(fromHex('c074323031332d30332d32315432303a30343a30305a'), { tags: { 0: dateDecoder } });
assert.instanceOf(decodedDate, Date);
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString());
});
it('Uint16Array as hex/23 (overide existing type)', () => {
assert.equal(toHex(encode(Uint16Array.from([
1,
2,
3
]), { typeEncoders: { Uint16Array: Uint16ArrayEncoder } })), 'd76c303130303032303030333030');
const decoded = decode(fromHex('d76c303130303032303030333030'), { tags: { 23: Uint16ArrayDecoder } });
assert.instanceOf(decoded, Uint16Array);
assert.equal(toHex(decoded), toHex(Uint16Array.from([
1,
2,
3
])));
});
it('tag int too large', () => {
const verify = (hex, strict) => {
if (!strict) {
assert.throws(() => decode(fromHex(hex), {
tags: { 8: dateDecoder },
strict: true
}), /integer encoded in more bytes than necessary/);
}
const decodedDate = decode(fromHex(hex), {
tags: { 8: dateDecoder },
strict
});
assert.instanceOf(decodedDate, Date);
assert.equal(decodedDate.toISOString(), new Date('2013-03-21T20:04:00Z').toISOString());
};
verify('c874323031332d30332d32315432303a30343a30305a', true);
verify('d80874323031332d30332d32315432303a30343a30305a', false);
verify('d9000874323031332d30332d32315432303a30343a30305a', false);
verify('da0000000874323031332d30332d32315432303a30343a30305a', false);
verify('db000000000000000874323031332d30332d32315432303a30343a30305a', false);
});
});
+250
View File
@@ -0,0 +1,250 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
const {assert} = chai;
const fixtures = [
{
data: '8601f5f4f6f720',
expected: [
1,
true,
false,
null,
undefined,
-1
],
type: 'array of float specials'
},
{
data: 'f93800',
expected: 0.5,
type: 'float16'
},
{
data: 'f9b800',
expected: -0.5,
type: 'float16'
},
{
data: 'fa33c00000',
expected: 8.940696716308594e-8,
type: 'float32'
},
{
data: 'fab3c00000',
expected: -8.940696716308594e-8,
type: 'float32'
},
{
data: 'fb3ff199999999999a',
expected: 1.1,
type: 'float64'
},
{
data: 'fbbff199999999999a',
expected: -1.1,
type: 'float64'
},
{
data: 'fb3ff1c71c71c71c72',
expected: 1.1111111111111112,
type: 'float64'
},
{
data: 'fb0000000000000002',
expected: 1e-323,
type: 'float64'
},
{
data: 'fb8000000000000002',
expected: -1e-323,
type: 'float64'
},
{
data: 'fb3fefffffffffffff',
expected: 0.9999999999999999,
type: 'float64'
},
{
data: 'fbbfefffffffffffff',
expected: -0.9999999999999999,
type: 'float64'
},
{
data: 'f97c00',
expected: Infinity,
type: 'Infinity'
},
{
data: 'fb7ff0000000000000',
expected: Infinity,
type: 'Infinity',
strict: false
},
{
data: 'f9fc00',
expected: -Infinity,
type: '-Infinity'
},
{
data: 'fbfff0000000000000',
expected: -Infinity,
type: '-Infinity',
strict: false
},
{
data: 'f97e00',
expected: NaN,
type: 'NaN'
},
{
data: 'f97ff8',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fa7ff80000',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb7ff8000000000000',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb7ff8cafedeadbeef',
expected: NaN,
type: 'NaN',
strict: false
},
{
data: 'fb40f4241a31a5a515',
expected: 82497.63712086187,
type: 'float64'
}
];
describe('float', () => {
describe('decode', () => {
for (const fixture of fixtures) {
const data = fromHex(fixture.data);
it(`should decode ${ fixture.type }=${ fixture.expected }`, () => {
assert.deepStrictEqual(decode(data), fixture.expected, `decode ${ fixture.type }`);
assert.deepStrictEqual(decode(data, { strict: true }), fixture.expected, `decode ${ fixture.type }`);
});
}
});
it('error', () => {
assert.throws(() => decode(fromHex('f80000')), Error, 'simple values are not supported');
assert.throws(() => decode(fromHex('f900')), Error, 'not enough data for float16');
assert.throws(() => decode(fromHex('fa0000')), Error, 'not enough data for float32');
assert.throws(() => decode(fromHex('fb00000000')), Error, 'not enough data for float64');
});
describe('encode', () => {
for (const fixture of fixtures) {
if (fixture.strict !== false) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
assert.strictEqual(toHex(encode(fixture.expected)), fixture.data, `encode ${ fixture.type }`);
});
}
}
});
describe('encode float64', () => {
for (const fixture of fixtures) {
if (fixture.type.startsWith('float')) {
it(`should encode ${ fixture.type }=${ fixture.expected }`, () => {
const encoded = encode(fixture.expected, { float64: true });
assert.strictEqual(encoded.length, 9);
assert.strictEqual(encoded[0], 251);
assert.strictEqual(decode(encoded), fixture.expected, `encode float64 ${ fixture.type }`);
});
}
}
});
describe('roundtrip', () => {
for (const fixture of fixtures) {
if (!fixture.unsafe && fixture.strict !== false) {
it(`should roundtrip ${ fixture.type }=${ fixture.expected }`, () => {
assert.deepStrictEqual(decode(encode(fixture.expected)), fixture.expected, `roundtrip ${ fixture.type }`);
});
}
}
});
describe('specials', () => {
it('indefinite length switch fails on BREAK', () => {
assert.throws(() => decode(Uint8Array.from([
131,
1,
2,
255
])), /unexpected break to lengthed array/);
assert.throws(() => decode(Uint8Array.from([
131,
1,
2,
255
]), { allowIndefinite: false }), /indefinite/);
});
it('can switch off undefined support', () => {
assert.deepStrictEqual(decode(fromHex('f7')), undefined);
assert.throws(() => decode(fromHex('f7'), { allowUndefined: false }), /undefined/);
assert.deepStrictEqual(decode(fromHex('830102f7')), [
1,
2,
undefined
]);
assert.throws(() => decode(fromHex('830102f7'), { allowUndefined: false }), /undefined/);
});
it('can coerce undefined to null', () => {
assert.deepStrictEqual(decode(fromHex('f7'), { coerceUndefinedToNull: false }), undefined);
assert.deepStrictEqual(decode(fromHex('f7'), { coerceUndefinedToNull: true }), null);
assert.deepStrictEqual(decode(fromHex('830102f7'), { coerceUndefinedToNull: false }), [
1,
2,
undefined
]);
assert.deepStrictEqual(decode(fromHex('830102f7'), { coerceUndefinedToNull: true }), [
1,
2,
null
]);
});
it('can switch off Infinity support', () => {
assert.deepStrictEqual(decode(fromHex('830102f97c00')), [
1,
2,
Infinity
]);
assert.deepStrictEqual(decode(fromHex('830102f9fc00')), [
1,
2,
-Infinity
]);
assert.throws(() => decode(fromHex('830102f97c00'), { allowInfinity: false }), /Infinity/);
assert.throws(() => decode(fromHex('830102f9fc00'), { allowInfinity: false }), /Infinity/);
for (const fixture of fixtures.filter(f => f.type.endsWith('Infinity'))) {
assert.throws(() => decode(fromHex(fixture.data), { allowInfinity: false }), /Infinity/);
}
});
it('can switch off NaN support', () => {
assert.deepStrictEqual(decode(fromHex('830102f97e00')), [
1,
2,
NaN
]);
assert.throws(() => decode(fromHex('830102f97e00'), { allowNaN: false }), /NaN/);
for (const fixture of fixtures.filter(f => f.type === 'NaN')) {
assert.throws(() => decode(fromHex(fixture.data), { allowNaN: false }), /NaN/);
}
});
});
});
+84
View File
@@ -0,0 +1,84 @@
import chai from 'chai';
import { Bl } from '../lib/bl.js';
const {assert} = chai;
describe('Internal bytes list', () => {
describe('push', () => {
it('push bits', () => {
const bl = new Bl(10);
const expected = [];
for (let i = 0; i < 25; i++) {
bl.push([i + 1]);
expected.push(i + 1);
}
assert.deepEqual([...bl.toBytes()], expected);
});
for (let i = 4; i < 21; i++) {
it(`push Bl(${ i })`, () => {
const bl = new Bl(i);
const expected = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
100,
110,
120,
11,
12,
130,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
];
for (let i = 0; i < 5; i++) {
bl.push([i + 1]);
}
bl.push(Uint8Array.from([
6,
7,
8,
9,
10
]));
bl.push([100]);
bl.push(Uint8Array.from([
110,
120
]));
bl.push(Uint8Array.from([
11,
12
]));
bl.push([130]);
bl.push(Uint8Array.from([
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
]));
assert.deepEqual([...bl.toBytes()], expected);
});
}
});
});
@@ -0,0 +1,95 @@
import chai from 'chai';
import {
decode,
encode
} from '../cborg.js';
import * as taglib from '../taglib.js';
import {
fromHex,
toHex
} from '../lib/byte-utils.js';
import { fixtures } from './appendix_a.js';
const {assert} = chai;
const tags = [];
const typeEncoders = {};
tags[0] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 1');
}
return `0("${ new Date(obj).toISOString().replace(/\.000Z$/, 'Z') }")`;
};
tags[1] = function (obj) {
if (typeof obj !== 'number') {
throw new Error('expected number for tag 1');
}
return `1(${ obj })`;
};
tags[2] = taglib.bigIntDecoder;
typeEncoders.bigint = taglib.bigIntEncoder;
tags[3] = taglib.bigNegIntDecoder;
tags[23] = function (obj) {
if (!(obj instanceof Uint8Array)) {
throw new Error('expected byte array for tag 23');
}
return `23(h'${ toHex(obj) }')`;
};
tags[24] = function (obj) {
return tags[23](obj).replace(/^23/, '24');
};
tags[32] = function (obj) {
if (typeof obj !== 'string') {
throw new Error('expected string for tag 32');
}
;
(() => new URL(obj))();
return `32("${ obj }")`;
};
describe('cbor/test-vectors', () => {
let i = 0;
for (const fixture of fixtures) {
const u8a = fromHex(fixture.hex);
let expected = fixture.decoded !== undefined ? fixture.decoded : fixture.diagnostic;
if (typeof expected === 'string' && expected.startsWith('h\'')) {
expected = fromHex(expected.replace(/(^h)'|('$)/g, ''));
}
it(`test vector #${ i }: ${ inspect(expected).replace(/\n\s*/g, '') }`, () => {
if (fixture.error) {
assert.throws(() => decode(u8a, { tags }), fixture.error);
} else {
if (fixture.noTagDecodeError) {
assert.throws(() => decode(u8a), fixture.noTagDecodeError);
}
let actual = decode(u8a, { tags });
if (typeof actual === 'bigint') {
actual = inspect(actual);
}
if (typeof expected === 'bigint') {
expected = inspect(expected);
}
assert.deepEqual(actual, expected);
if (fixture.roundtrip) {
if (fixture.noTagEncodeError) {
assert.throws(() => encode(decode(u8a, { tags })), fixture.noTagEncodeError);
}
const reencoded = encode(decode(u8a, { tags }), { typeEncoders });
assert.equal(toHex(reencoded), fixture.hex);
}
}
});
i++;
}
it.skip('encode w/ tags', () => {
});
});
function inspect(o) {
if (typeof o === 'string') {
return `'${ o }'`;
}
if (o instanceof Uint8Array) {
return `Uint8Array<${ o.join(',') }>`;
}
if (o == null || typeof o !== 'object') {
return String(o);
}
return JSON.stringify(o);
}
@@ -0,0 +1,69 @@
import chai from 'chai';
import { decode } from '../cborg.js';
import { fromHex } from '../lib/byte-utils.js';
const {assert} = chai;
describe('decode errors', () => {
it('not Uint8Array', () => {
for (const arg of [
true,
false,
null,
undefined,
'string',
{ obj: 'ect' },
{},
['array'],
[],
[
1,
2,
3
],
0,
100,
1.1,
-1,
Symbol.for('nope')
]) {
assert.throws(() => decode(arg), /CBOR decode error.*must be a Uint8Array/);
}
});
it('no data', () => {
assert.throws(() => decode(new Uint8Array('')), /CBOR decode error.*content/);
});
it('break only', () => {
assert.throws(() => decode(new Uint8Array([255])), /CBOR decode error.*break/);
});
it('not enough map entries (value)', () => {
assert.throws(() => decode(fromHex('a2616f016174')), /map.*not enough entries.*value/);
});
it('not enough map entries (key)', () => {
assert.throws(() => decode(fromHex('a2616f01')), /map.*not enough entries.*key/);
});
it('break in lengthed map', () => {
assert.throws(() => decode(fromHex('a2616f01ff740f')), /unexpected break to lengthed map/);
});
it('not enough array entries', () => {
assert.throws(() => decode(fromHex('82616f')), /array.*not enough entries/);
});
it('break in lengthed array', () => {
assert.throws(() => decode(fromHex('82ff')), /unexpected break to lengthed array/);
});
it('no such decoder', () => {
assert.throws(() => decode(fromHex('82ff')), /unexpected break to lengthed array/);
});
it('too many terminals', () => {
assert.throws(() => decode(fromHex('0101')), /too many terminals/);
});
it('rejectDuplicateMapKeys enabled on duplicate keys', () => {
assert.deepStrictEqual(decode(fromHex('a3636261720363666f6f0163666f6f02')), {
foo: 2,
bar: 3
});
assert.throws(() => decode(fromHex('a3636261720363666f6f0163666f6f02'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/);
assert.throws(() => decode(fromHex('a3636261720363666f6f0163666f6f02'), {
useMaps: true,
rejectDuplicateMapKeys: true
}), /CBOR decode error: found repeat map key "foo"/);
});
});
+50
View File
@@ -0,0 +1,50 @@
import { garbage } from 'ipld-garbage';
import {
decode,
encode
} from '../cborg.js';
import chai from 'chai';
const {assert} = chai;
describe('Fuzz round-trip', () => {
it('random objects', function () {
this.timeout(5000);
for (let i = 0; i < 1000; i++) {
const obj = garbage(300, { weights: { CID: 0 } });
const byts = encode(obj);
const decoded = decode(byts);
assert.deepEqual(decoded, obj);
}
});
it('circular references error', () => {
let obj = {};
obj.obj = obj;
assert.throws(() => encode(obj), /circular references/);
obj = {
blip: [
1,
2,
{ blop: {} }
]
};
obj.blip[2].blop.boop = obj;
assert.throws(() => encode(obj), /circular references/);
obj = {
blip: [
1,
2,
{ blop: {} }
]
};
obj.blip[2].blop.boop = obj.blip;
assert.throws(() => encode(obj), /circular references/);
obj = {
blip: {},
bloop: {}
};
obj.bloop = obj.blip;
assert.doesNotThrow(() => encode(obj));
const arr = [];
arr[0] = arr;
assert.throws(() => encode(arr), /circular references/);
});
});
+279
View File
@@ -0,0 +1,279 @@
import { assert } from 'chai';
import {
decode,
encode
} from '../lib/json/json.js';
const toBytes = str => new TextEncoder().encode(str);
function verifyRoundTrip(obj, sorting) {
const encoded = new TextDecoder().decode(encode(obj, sorting === false ? { mapSorter: null } : undefined));
const json = JSON.stringify(obj);
assert.strictEqual(encoded, json);
const decoded = decode(toBytes(JSON.stringify(obj)));
assert.deepStrictEqual(decoded, obj);
}
function verifyEncodedForm(testCase) {
const obj = JSON.parse(testCase);
const encoded = encode(obj);
assert.strictEqual(new TextDecoder().decode(encoded), JSON.stringify(obj));
const decoded = decode(encoded);
assert.deepStrictEqual(decoded, obj);
const decoded2 = decode(toBytes(testCase));
assert.deepStrictEqual(decoded2, obj);
}
describe('json basics', () => {
it('can round-trip basic literals', () => {
const testCases = [
'null',
'true',
'false',
'0',
'9007199254740991',
'-9007199254740991',
JSON.stringify(Number.MAX_VALUE),
JSON.stringify(Number.MIN_VALUE)
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
assert.strictEqual(decode(toBytes('1E1')), 10);
assert.strictEqual(decode(toBytes('0.1e1')), 1);
assert.strictEqual(decode(toBytes('1e-1')), 0.1);
assert.strictEqual(decode(toBytes('1e+00')), 1);
assert.strictEqual(decode(toBytes('10.0')), 10);
assert.deepStrictEqual(decode(toBytes('[-10.0,1.0,0.0,100.0]')), [
-10,
1,
0,
100
]);
verifyRoundTrip(true);
verifyRoundTrip(false);
verifyRoundTrip(null);
verifyRoundTrip(100);
verifyRoundTrip(-100);
verifyRoundTrip(1.11);
verifyRoundTrip(-100.11111);
verifyRoundTrip(11100000000);
verifyRoundTrip(1.0011111e-18);
});
it('handles large integers as BigInt', () => {
const verify = (inp, str) => {
if (str === undefined) {
str = String(inp);
}
assert.strictEqual(decode(toBytes(str), { allowBigInt: true }), inp);
assert.strictEqual(decode(toBytes(str)), parseFloat(str));
};
verify(Number.MAX_SAFE_INTEGER);
verify(-Number.MAX_SAFE_INTEGER);
verify(BigInt('9007199254740992'));
verify(BigInt('9007199254740993'));
verify(BigInt('11959030306112471731'));
verify(BigInt('18446744073709551615'));
verify(BigInt('9223372036854775807'));
verify(BigInt('-9007199254740992'));
verify(BigInt('-9007199254740993'));
verify(BigInt('-9223372036854776000'));
verify(BigInt('-11959030306112471732'));
verify(BigInt('-18446744073709551616'));
verify(-9007199254740992, '-9007199254740992.0');
verify(-9223372036854776000, '-9223372036854776000.0');
verify(-18446744073709552000, '-18446744073709551616.0');
});
it('can round-trip string literals', () => {
const testCases = [
JSON.stringify(''),
JSON.stringify(' '),
JSON.stringify('"'),
JSON.stringify('\\'),
JSON.stringify('\b\f\n\r\t'),
JSON.stringify('"'),
JSON.stringify('&#34; %22 0x22 034 &#x22;'),
'"\uD83D\uDE00"'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
assert.strictEqual(decode(toBytes('"/ & \\/"')), '/ & /');
verifyRoundTrip('this is a string');
verifyRoundTrip('this \uD834\uDD1E is a \u263A\u263A \u2663 string ̐ ̀\n\r');
verifyRoundTrip('');
verifyRoundTrip('foo\\bar\nbaz\tbop\rbing"bip\'bang');
});
it('can round-trip array literals', () => {
const testCases = [
'[]',
'[null]',
'[true, false]',
'[ \n 0,1, 2\n , 3,\n4] \n ',
'[-10.0, 1.0, 0.0, 100.0]',
'[["2 deep"]]'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
verifyRoundTrip([
1,
2,
3,
'string',
true,
4
]);
verifyRoundTrip([
1,
2,
3,
'string',
true,
[
'and',
'a',
'nested',
'array',
true
],
4
]);
});
it('can round-trip object literals', () => {
const testCases = [
'{}',
'\n {\n "\\b"\n :\n""\n }\n ',
'{"":""}',
'{"1":{"2":0,"3":"deep"}}'
];
for (const testCase of testCases) {
verifyEncodedForm(testCase);
}
});
it('will sort map keys', () => {
const unsorted = {
one: 1,
two: 2,
three: 3.1,
str: 'string',
bool: true,
four: 4
};
verifyRoundTrip(unsorted, false);
assert.strictEqual(new TextDecoder().decode(encode(unsorted)), '{"bool":true,"four":4,"one":1,"str":"string","three":3.1,"two":2}');
});
it('can handle novel cases', () => {
assert.strictEqual(decode(toBytes('"this \\uD834\\uDD1E is a \\u263a\\u263a string"')), 'this \uD834\uDD1E is a \u263A\u263A string');
verifyRoundTrip({
one: 1,
two: 2,
three: 3.1,
str: 'string',
arr: [
'and',
'a',
'nested',
[],
'array',
[
true,
1
],
false
],
bool: true,
obj: {
nested: 'object',
a: [],
o: {}
},
four: 4
}, false);
verifyRoundTrip([
false,
[
{
'#nFzU': {},
'\\w>': -0.9441451951197325,
'\t\'': '\'JB+2Wg\tw"IrM*#e^L/d&4rrzUuwq(1mH6aVRredB&Bfs]S"KqK(Tz1Q"URBAfw',
'\n@FrfM': 'M[D]q&'
},
'J4>\'Xdc+u2$%',
4227406737130333
]
], false);
verifyRoundTrip([
0.12995619865708727,
-4973404279772543,
{
drG2: [true],
';#K^Qf>V': null,
'`2=': 'ecc<e/$+-.;U>Gr5RdZDJ\n5+:{=QHNN.tVVN~dX$FWFwu`6>"&=tW!*1*^\u263A)JFM1p|}&X.B|${*\\f@!w2\u263A+'
}
], false);
assert.strictEqual(`${ decode(encode(9007199254740991)) }`, '9007199254740991');
assert.strictEqual(`${ decode(encode(9007199254740992)) }`, '9007199254740992');
assert.strictEqual(`${ decode(encode(900719925474099100n)) }`, '900719925474099100');
});
it('should throw on bad types', () => {
assert.throws(() => encode(new Uint8Array([
1,
2
])), /CBOR encode error: unsupported type: Uint8Array/);
assert.throws(() => encode({
boop: new Uint8Array([
1,
2
])
}), /CBOR encode error: unsupported type: Uint8Array/);
assert.throws(() => encode(undefined), /CBOR encode error: unsupported type: undefined/);
assert.throws(() => encode(new Map([
[
1,
2
],
[
2,
3
]
])), /CBOR encode error: non-string map keys are not supported/);
assert.throws(() => encode(new Map([
[
[
'foo',
'bar'
],
2
],
[
[
'bar',
'foo'
],
3
]
])), /CBOR encode error: complex map keys are not supported/);
});
it('should throw on bad decode failure modes', () => {
assert.throws(() => decode(toBytes('{"a":1 & "b":2}')), 'CBOR decode error: unexpected character at position 7, was expecting object delimiter but found \'&\'');
assert.throws(() => decode(toBytes('{"a":1,"b"!2}')), 'CBOR decode error: unexpected character at position 10, was expecting key/value delimiter \':\' but found \'!\'');
assert.throws(() => decode(toBytes('[1,2&3]')), 'CBOR decode error: unexpected character at position 4, was expecting array delimiter but found \'&\'');
assert.throws(() => decode(toBytes('{"a":!}')), 'CBOR decode error: unexpected character at position 5');
assert.throws(() => decode(toBytes('"abc')), 'CBOR decode error: unexpected end of string at position 4');
assert.throws(() => decode(toBytes('"ab\\xc"')), 'CBOR decode error: unexpected string escape character at position 5');
assert.throws(() => decode(toBytes('"ab\x1Ec"')), 'CBOR decode error: invalid control character at position 3');
assert.throws(() => decode(toBytes('"ab\\')), 'CBOR decode error: unexpected string termination at position 4');
assert.throws(() => decode(toBytes('"\u263A').subarray(0, 3)), 'CBOR decode error: unexpected unicode sequence at position 1');
assert.throws(() => decode(toBytes('"\\uxyza"')), 'CBOR decode error: unexpected unicode escape character at position 3');
assert.throws(() => decode(toBytes('"\\u11"')), 'CBOR decode error: unexpected end of unicode escape sequence at position 3');
assert.throws(() => decode(toBytes('-boop')), 'CBOR decode error: unexpected token at position 1');
assert.throws(() => decode(toBytes('{"v":nope}')), 'CBOR decode error: unexpected token at position 7, expected to find \'null\'');
assert.throws(() => decode(toBytes('[n]')), 'CBOR decode error: unexpected end of input at position 1');
assert.throws(() => decode(toBytes('{"v":truu}')), 'CBOR decode error: unexpected token at position 9, expected to find \'true\'');
assert.throws(() => decode(toBytes('[tr]')), 'CBOR decode error: unexpected end of input at position 1');
assert.throws(() => decode(toBytes('{"v":flase}')), 'CBOR decode error: unexpected token at position 7, expected to find \'false\'');
assert.throws(() => decode(toBytes('[fa]')), 'CBOR decode error: unexpected end of input at position 1');
assert.throws(() => decode(toBytes('-0..1')), 'CBOR decode error: unexpected token at position 3');
});
it('should throw when rejectDuplicateMapKeys enabled on duplicate keys', () => {
assert.deepStrictEqual(decode(toBytes('{"foo":1,"foo":2}')), { foo: 2 });
assert.throws(() => decode(toBytes('{"foo":1,"foo":2}'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/);
});
});
+55
View File
@@ -0,0 +1,55 @@
import chai from 'chai';
import { garbage } from 'ipld-garbage';
import { uintBoundaries } from '../lib/0uint.js';
import { encode } from '../cborg.js';
import { encodedLength } from '../lib/length.js';
import { dateEncoder } from './common.js';
const {assert} = chai;
function verifyLength(object, options) {
const len = encodedLength(object, options);
const encoded = encode(object, options);
const actual = encoded.length;
assert.strictEqual(actual, len, JSON.stringify(object));
}
describe('encodedLength', () => {
it('int boundaries', () => {
for (let ii = 0; ii < 4; ii++) {
verifyLength(uintBoundaries[ii]);
verifyLength(uintBoundaries[ii] - 1);
verifyLength(uintBoundaries[ii] + 1);
verifyLength(-1 * uintBoundaries[ii]);
verifyLength(-1 * uintBoundaries[ii] - 1);
verifyLength(-1 * uintBoundaries[ii] + 1);
}
});
it('tags', () => {
verifyLength({ date: new Date('2013-03-21T20:04:00Z') }, { typeEncoders: { Date: dateEncoder } });
});
it('floats', () => {
verifyLength(0.5);
verifyLength(0.5, { float64: true });
verifyLength(8.940696716308594e-8);
verifyLength(8.940696716308594e-8, { float64: true });
});
it('small garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 1000; ii++) {
const gbg = garbage(1 << 6, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
it('medium garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 100; ii++) {
const gbg = garbage(1 << 16, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
it('large garbage', function () {
this.timeout(10000);
for (let ii = 0; ii < 10; ii++) {
const gbg = garbage(1 << 20, { weights: { CID: 0 } });
verifyLength(gbg);
}
});
});
+12
View File
@@ -0,0 +1,12 @@
import { encode } from './lib/encode.js';
import { decode } from './lib/decode.js';
import {
Token,
Type
} from './lib/token.js';
export {
decode,
encode,
Token,
Type
};
+152
View File
@@ -0,0 +1,152 @@
import {
Token,
Type
} from './token.js';
import {
decodeErrPrefix,
assertEnoughData
} from './common.js';
export const uintBoundaries = [
24,
256,
65536,
4294967296,
BigInt('18446744073709551616')
];
export function readUint8(data, offset, options) {
assertEnoughData(data, offset, 1);
const value = data[offset];
if (options.strict === true && value < uintBoundaries[0]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
export function readUint16(data, offset, options) {
assertEnoughData(data, offset, 2);
const value = data[offset] << 8 | data[offset + 1];
if (options.strict === true && value < uintBoundaries[1]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
export function readUint32(data, offset, options) {
assertEnoughData(data, offset, 4);
const value = data[offset] * 16777216 + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3];
if (options.strict === true && value < uintBoundaries[2]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
export function readUint64(data, offset, options) {
assertEnoughData(data, offset, 8);
const hi = data[offset] * 16777216 + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3];
const lo = data[offset + 4] * 16777216 + (data[offset + 5] << 16) + (data[offset + 6] << 8) + data[offset + 7];
const value = (BigInt(hi) << BigInt(32)) + BigInt(lo);
if (options.strict === true && value < uintBoundaries[3]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
if (value <= Number.MAX_SAFE_INTEGER) {
return Number(value);
}
if (options.allowBigInt === true) {
return value;
}
throw new Error(`${ decodeErrPrefix } integers outside of the safe integer range are not supported`);
}
export function decodeUint8(data, pos, _minor, options) {
return new Token(Type.uint, readUint8(data, pos + 1, options), 2);
}
export function decodeUint16(data, pos, _minor, options) {
return new Token(Type.uint, readUint16(data, pos + 1, options), 3);
}
export function decodeUint32(data, pos, _minor, options) {
return new Token(Type.uint, readUint32(data, pos + 1, options), 5);
}
export function decodeUint64(data, pos, _minor, options) {
return new Token(Type.uint, readUint64(data, pos + 1, options), 9);
}
export function encodeUint(buf, token) {
return encodeUintValue(buf, 0, token.value);
}
export function encodeUintValue(buf, major, uint) {
if (uint < uintBoundaries[0]) {
const nuint = Number(uint);
buf.push([major | nuint]);
} else if (uint < uintBoundaries[1]) {
const nuint = Number(uint);
buf.push([
major | 24,
nuint
]);
} else if (uint < uintBoundaries[2]) {
const nuint = Number(uint);
buf.push([
major | 25,
nuint >>> 8,
nuint & 255
]);
} else if (uint < uintBoundaries[3]) {
const nuint = Number(uint);
buf.push([
major | 26,
nuint >>> 24 & 255,
nuint >>> 16 & 255,
nuint >>> 8 & 255,
nuint & 255
]);
} else {
const buint = BigInt(uint);
if (buint < uintBoundaries[4]) {
const set = [
major | 27,
0,
0,
0,
0,
0,
0,
0
];
let lo = Number(buint & BigInt(4294967295));
let hi = Number(buint >> BigInt(32) & BigInt(4294967295));
set[8] = lo & 255;
lo = lo >> 8;
set[7] = lo & 255;
lo = lo >> 8;
set[6] = lo & 255;
lo = lo >> 8;
set[5] = lo & 255;
set[4] = hi & 255;
hi = hi >> 8;
set[3] = hi & 255;
hi = hi >> 8;
set[2] = hi & 255;
hi = hi >> 8;
set[1] = hi & 255;
buf.push(set);
} else {
throw new Error(`${ decodeErrPrefix } encountered BigInt larger than allowable range`);
}
}
}
encodeUint.encodedSize = function encodedSize(token) {
return encodeUintValue.encodedSize(token.value);
};
encodeUintValue.encodedSize = function encodedSize(uint) {
if (uint < uintBoundaries[0]) {
return 1;
}
if (uint < uintBoundaries[1]) {
return 2;
}
if (uint < uintBoundaries[2]) {
return 3;
}
if (uint < uintBoundaries[3]) {
return 5;
}
return 9;
};
encodeUint.compareTokens = function compareTokens(tok1, tok2) {
return tok1.value < tok2.value ? -1 : tok1.value > tok2.value ? 1 : 0;
};
+55
View File
@@ -0,0 +1,55 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import { decodeErrPrefix } from './common.js';
export function decodeNegint8(data, pos, _minor, options) {
return new Token(Type.negint, -1 - uint.readUint8(data, pos + 1, options), 2);
}
export function decodeNegint16(data, pos, _minor, options) {
return new Token(Type.negint, -1 - uint.readUint16(data, pos + 1, options), 3);
}
export function decodeNegint32(data, pos, _minor, options) {
return new Token(Type.negint, -1 - uint.readUint32(data, pos + 1, options), 5);
}
const neg1b = BigInt(-1);
const pos1b = BigInt(1);
export function decodeNegint64(data, pos, _minor, options) {
const int = uint.readUint64(data, pos + 1, options);
if (typeof int !== 'bigint') {
const value = -1 - int;
if (value >= Number.MIN_SAFE_INTEGER) {
return new Token(Type.negint, value, 9);
}
}
if (options.allowBigInt !== true) {
throw new Error(`${ decodeErrPrefix } integers outside of the safe integer range are not supported`);
}
return new Token(Type.negint, neg1b - BigInt(int), 9);
}
export function encodeNegint(buf, token) {
const negint = token.value;
const unsigned = typeof negint === 'bigint' ? negint * neg1b - pos1b : negint * -1 - 1;
uint.encodeUintValue(buf, token.type.majorEncoded, unsigned);
}
encodeNegint.encodedSize = function encodedSize(token) {
const negint = token.value;
const unsigned = typeof negint === 'bigint' ? negint * neg1b - pos1b : negint * -1 - 1;
if (unsigned < uint.uintBoundaries[0]) {
return 1;
}
if (unsigned < uint.uintBoundaries[1]) {
return 2;
}
if (unsigned < uint.uintBoundaries[2]) {
return 3;
}
if (unsigned < uint.uintBoundaries[3]) {
return 5;
}
return 9;
};
encodeNegint.compareTokens = function compareTokens(tok1, tok2) {
return tok1.value < tok2.value ? 1 : tok1.value > tok2.value ? -1 : 0;
};
+59
View File
@@ -0,0 +1,59 @@
import {
Token,
Type
} from './token.js';
import {
assertEnoughData,
decodeErrPrefix
} from './common.js';
import * as uint from './0uint.js';
import {
compare,
fromString,
slice
} from './byte-utils.js';
function toToken(data, pos, prefix, length) {
assertEnoughData(data, pos, prefix + length);
const buf = slice(data, pos + prefix, pos + prefix + length);
return new Token(Type.bytes, buf, prefix + length);
}
export function decodeBytesCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
export function decodeBytes8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
}
export function decodeBytes16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
}
export function decodeBytes32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
}
export function decodeBytes64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer bytes lengths not supported`);
}
return toToken(data, pos, 9, l);
}
function tokenBytes(token) {
if (token.encodedBytes === undefined) {
token.encodedBytes = token.type === Type.string ? fromString(token.value) : token.value;
}
return token.encodedBytes;
}
export function encodeBytes(buf, token) {
const bytes = tokenBytes(token);
uint.encodeUintValue(buf, token.type.majorEncoded, bytes.length);
buf.push(bytes);
}
encodeBytes.encodedSize = function encodedSize(token) {
const bytes = tokenBytes(token);
return uint.encodeUintValue.encodedSize(bytes.length) + bytes.length;
};
encodeBytes.compareTokens = function compareTokens(tok1, tok2) {
return compareBytes(tokenBytes(tok1), tokenBytes(tok2));
};
export function compareBytes(b1, b2) {
return b1.length < b2.length ? -1 : b1.length > b2.length ? 1 : compare(b1, b2);
}
+43
View File
@@ -0,0 +1,43 @@
import {
Token,
Type
} from './token.js';
import {
assertEnoughData,
decodeErrPrefix
} from './common.js';
import * as uint from './0uint.js';
import { encodeBytes } from './2bytes.js';
import {
toString,
slice
} from './byte-utils.js';
function toToken(data, pos, prefix, length, options) {
const totLength = prefix + length;
assertEnoughData(data, pos, totLength);
const tok = new Token(Type.string, toString(data, pos + prefix, pos + totLength), totLength);
if (options.retainStringBytes === true) {
tok.byteValue = slice(data, pos + prefix, pos + totLength);
}
return tok;
}
export function decodeStringCompact(data, pos, minor, options) {
return toToken(data, pos, 1, minor, options);
}
export function decodeString8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options), options);
}
export function decodeString16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options), options);
}
export function decodeString32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options), options);
}
export function decodeString64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer string lengths not supported`);
}
return toToken(data, pos, 9, l, options);
}
export const encodeString = encodeBytes;
+41
View File
@@ -0,0 +1,41 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import { decodeErrPrefix } from './common.js';
function toToken(_data, _pos, prefix, length) {
return new Token(Type.array, length, prefix);
}
export function decodeArrayCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
export function decodeArray8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
}
export function decodeArray16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
}
export function decodeArray32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
}
export function decodeArray64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer array lengths not supported`);
}
return toToken(data, pos, 9, l);
}
export function decodeArrayIndefinite(data, pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ decodeErrPrefix } indefinite length items not allowed`);
}
return toToken(data, pos, 1, Infinity);
}
export function encodeArray(buf, token) {
uint.encodeUintValue(buf, Type.array.majorEncoded, token.value);
}
encodeArray.compareTokens = uint.encodeUint.compareTokens;
encodeArray.encodedSize = function encodedSize(token) {
return uint.encodeUintValue.encodedSize(token.value);
};
+41
View File
@@ -0,0 +1,41 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import { decodeErrPrefix } from './common.js';
function toToken(_data, _pos, prefix, length) {
return new Token(Type.map, length, prefix);
}
export function decodeMapCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
export function decodeMap8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
}
export function decodeMap16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
}
export function decodeMap32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
}
export function decodeMap64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer map lengths not supported`);
}
return toToken(data, pos, 9, l);
}
export function decodeMapIndefinite(data, pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ decodeErrPrefix } indefinite length items not allowed`);
}
return toToken(data, pos, 1, Infinity);
}
export function encodeMap(buf, token) {
uint.encodeUintValue(buf, Type.map.majorEncoded, token.value);
}
encodeMap.compareTokens = uint.encodeUint.compareTokens;
encodeMap.encodedSize = function encodedSize(token) {
return uint.encodeUintValue.encodedSize(token.value);
};
+27
View File
@@ -0,0 +1,27 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
export function decodeTagCompact(_data, _pos, minor, _options) {
return new Token(Type.tag, minor, 1);
}
export function decodeTag8(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint8(data, pos + 1, options), 2);
}
export function decodeTag16(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint16(data, pos + 1, options), 3);
}
export function decodeTag32(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint32(data, pos + 1, options), 5);
}
export function decodeTag64(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint64(data, pos + 1, options), 9);
}
export function encodeTag(buf, token) {
uint.encodeUintValue(buf, Type.tag.majorEncoded, token.value);
}
encodeTag.compareTokens = uint.encodeUint.compareTokens;
encodeTag.encodedSize = function encodedSize(token) {
return uint.encodeUintValue.encodedSize(token.value);
};
+179
View File
@@ -0,0 +1,179 @@
import {
Token,
Type
} from './token.js';
import { decodeErrPrefix } from './common.js';
import { encodeUint } from './0uint.js';
const MINOR_FALSE = 20;
const MINOR_TRUE = 21;
const MINOR_NULL = 22;
const MINOR_UNDEFINED = 23;
export function decodeUndefined(_data, _pos, _minor, options) {
if (options.allowUndefined === false) {
throw new Error(`${ decodeErrPrefix } undefined values are not supported`);
} else if (options.coerceUndefinedToNull === true) {
return new Token(Type.null, null, 1);
}
return new Token(Type.undefined, undefined, 1);
}
export function decodeBreak(_data, _pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ decodeErrPrefix } indefinite length items not allowed`);
}
return new Token(Type.break, undefined, 1);
}
function createToken(value, bytes, options) {
if (options) {
if (options.allowNaN === false && Number.isNaN(value)) {
throw new Error(`${ decodeErrPrefix } NaN values are not supported`);
}
if (options.allowInfinity === false && (value === Infinity || value === -Infinity)) {
throw new Error(`${ decodeErrPrefix } Infinity values are not supported`);
}
}
return new Token(Type.float, value, bytes);
}
export function decodeFloat16(data, pos, _minor, options) {
return createToken(readFloat16(data, pos + 1), 3, options);
}
export function decodeFloat32(data, pos, _minor, options) {
return createToken(readFloat32(data, pos + 1), 5, options);
}
export function decodeFloat64(data, pos, _minor, options) {
return createToken(readFloat64(data, pos + 1), 9, options);
}
export function encodeFloat(buf, token, options) {
const float = token.value;
if (float === false) {
buf.push([Type.float.majorEncoded | MINOR_FALSE]);
} else if (float === true) {
buf.push([Type.float.majorEncoded | MINOR_TRUE]);
} else if (float === null) {
buf.push([Type.float.majorEncoded | MINOR_NULL]);
} else if (float === undefined) {
buf.push([Type.float.majorEncoded | MINOR_UNDEFINED]);
} else {
let decoded;
let success = false;
if (!options || options.float64 !== true) {
encodeFloat16(float);
decoded = readFloat16(ui8a, 1);
if (float === decoded || Number.isNaN(float)) {
ui8a[0] = 249;
buf.push(ui8a.slice(0, 3));
success = true;
} else {
encodeFloat32(float);
decoded = readFloat32(ui8a, 1);
if (float === decoded) {
ui8a[0] = 250;
buf.push(ui8a.slice(0, 5));
success = true;
}
}
}
if (!success) {
encodeFloat64(float);
decoded = readFloat64(ui8a, 1);
ui8a[0] = 251;
buf.push(ui8a.slice(0, 9));
}
}
}
encodeFloat.encodedSize = function encodedSize(token, options) {
const float = token.value;
if (float === false || float === true || float === null || float === undefined) {
return 1;
}
if (!options || options.float64 !== true) {
encodeFloat16(float);
let decoded = readFloat16(ui8a, 1);
if (float === decoded || Number.isNaN(float)) {
return 3;
}
encodeFloat32(float);
decoded = readFloat32(ui8a, 1);
if (float === decoded) {
return 5;
}
}
return 9;
};
const buffer = new ArrayBuffer(9);
const dataView = new DataView(buffer, 1);
const ui8a = new Uint8Array(buffer, 0);
function encodeFloat16(inp) {
if (inp === Infinity) {
dataView.setUint16(0, 31744, false);
} else if (inp === -Infinity) {
dataView.setUint16(0, 64512, false);
} else if (Number.isNaN(inp)) {
dataView.setUint16(0, 32256, false);
} else {
dataView.setFloat32(0, inp);
const valu32 = dataView.getUint32(0);
const exponent = (valu32 & 2139095040) >> 23;
const mantissa = valu32 & 8388607;
if (exponent === 255) {
dataView.setUint16(0, 31744, false);
} else if (exponent === 0) {
dataView.setUint16(0, (inp & 2147483648) >> 16 | mantissa >> 13, false);
} else {
const logicalExponent = exponent - 127;
if (logicalExponent < -24) {
dataView.setUint16(0, 0);
} else if (logicalExponent < -14) {
dataView.setUint16(0, (valu32 & 2147483648) >> 16 | 1 << 24 + logicalExponent, false);
} else {
dataView.setUint16(0, (valu32 & 2147483648) >> 16 | logicalExponent + 15 << 10 | mantissa >> 13, false);
}
}
}
}
function readFloat16(ui8a, pos) {
if (ui8a.length - pos < 2) {
throw new Error(`${ decodeErrPrefix } not enough data for float16`);
}
const half = (ui8a[pos] << 8) + ui8a[pos + 1];
if (half === 31744) {
return Infinity;
}
if (half === 64512) {
return -Infinity;
}
if (half === 32256) {
return NaN;
}
const exp = half >> 10 & 31;
const mant = half & 1023;
let val;
if (exp === 0) {
val = mant * 2 ** -24;
} else if (exp !== 31) {
val = (mant + 1024) * 2 ** (exp - 25);
} else {
val = mant === 0 ? Infinity : NaN;
}
return half & 32768 ? -val : val;
}
function encodeFloat32(inp) {
dataView.setFloat32(0, inp, false);
}
function readFloat32(ui8a, pos) {
if (ui8a.length - pos < 4) {
throw new Error(`${ decodeErrPrefix } not enough data for float32`);
}
const offset = (ui8a.byteOffset || 0) + pos;
return new DataView(ui8a.buffer, offset, 4).getFloat32(0, false);
}
function encodeFloat64(inp) {
dataView.setFloat64(0, inp, false);
}
function readFloat64(ui8a, pos) {
if (ui8a.length - pos < 8) {
throw new Error(`${ decodeErrPrefix } not enough data for float64`);
}
const offset = (ui8a.byteOffset || 0) + pos;
return new DataView(ui8a.buffer, offset, 8).getFloat64(0, false);
}
encodeFloat.compareTokens = encodeUint.compareTokens;
+137
View File
@@ -0,0 +1,137 @@
import process from 'process';
import {
decode,
encode
} from '../cborg.js';
import {
tokensToDiagnostic,
fromDiag
} from './diagnostic.js';
import {
fromHex as _fromHex,
toHex
} from './byte-utils.js';
function usage(code) {
console.error('Usage: cborg <command> <args>');
console.error('Valid commands:');
console.error('\tbin2diag [binary input]');
console.error('\tbin2hex [binary input]');
console.error('\tbin2json [--pretty] [binary input]');
console.error('\tdiag2bin [diagnostic input]');
console.error('\tdiag2hex [diagnostic input]');
console.error('\tdiag2json [--pretty] [diagnostic input]');
console.error('\thex2bin [hex input]');
console.error('\thex2diag [hex input]');
console.error('\thex2json [--pretty] [hex input]');
console.error('\tjson2bin \'[json input]\'');
console.error('\tjson2diag \'[json input]\'');
console.error('\tjson2hex \'[json input]\'');
console.error('Input may either be supplied as an argument or piped via stdin');
process.exit(code || 0);
}
async function fromStdin() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
}
function fromHex(str) {
str = str.replace(/\r?\n/g, '');
if (!/^([0-9a-f]{2})*$/i.test(str)) {
throw new Error('Input string is not hexadecimal format');
}
return _fromHex(str);
}
function argvPretty() {
const argv = process.argv.filter(s => s !== '--pretty');
const pretty = argv.length !== process.argv.length;
return {
argv,
pretty
};
}
async function run() {
const cmd = process.argv[2];
switch (cmd) {
case 'help': {
return usage(0);
}
case 'bin2diag': {
const bin = process.argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process.argv[3]);
for (const line of tokensToDiagnostic(bin)) {
console.log(line);
}
return;
}
case 'bin2hex': {
const bin = process.argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process.argv[3]);
return console.log(toHex(bin));
}
case 'bin2json': {
const {argv, pretty} = argvPretty();
const bin = argv.length < 4 ? await fromStdin() : new TextEncoder().encode(argv[3]);
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined));
}
case 'diag2bin': {
const bin = fromDiag(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
return process.stdout.write(bin);
}
case 'diag2hex': {
const bin = fromDiag(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
return console.log(toHex(bin));
}
case 'diag2json': {
const {argv, pretty} = argvPretty();
const bin = fromDiag(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined));
}
case 'hex2bin': {
const bin = fromHex(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
return process.stdout.write(bin);
}
case 'hex2diag': {
const bin = fromHex(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
for (const line of tokensToDiagnostic(bin)) {
console.log(line);
}
return;
}
case 'hex2json': {
const {argv, pretty} = argvPretty();
const bin = fromHex(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined));
}
case 'json2bin': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3];
const obj = JSON.parse(inp);
return process.stdout.write(encode(obj));
}
case 'json2diag': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3];
const obj = JSON.parse(inp);
for (const line of tokensToDiagnostic(encode(obj))) {
console.log(line);
}
return;
}
case 'json2hex': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3];
const obj = JSON.parse(inp);
return console.log(toHex(encode(obj)));
}
default: {
if (process.argv.findIndex(a => a.endsWith('mocha')) === -1) {
if (cmd) {
console.error(`Unknown command: '${ cmd }'`);
}
usage(1);
}
}
}
}
run().catch(err => {
console.error(err);
process.exit(1);
});
export default true;
+74
View File
@@ -0,0 +1,74 @@
import {
alloc,
concat,
slice
} from './byte-utils.js';
const defaultChunkSize = 256;
export class Bl {
constructor(chunkSize = defaultChunkSize) {
this.chunkSize = chunkSize;
this.cursor = 0;
this.maxCursor = -1;
this.chunks = [];
this._initReuseChunk = null;
}
reset() {
this.cursor = 0;
this.maxCursor = -1;
if (this.chunks.length) {
this.chunks = [];
}
if (this._initReuseChunk !== null) {
this.chunks.push(this._initReuseChunk);
this.maxCursor = this._initReuseChunk.length - 1;
}
}
push(bytes) {
let topChunk = this.chunks[this.chunks.length - 1];
const newMax = this.cursor + bytes.length;
if (newMax <= this.maxCursor + 1) {
const chunkPos = topChunk.length - (this.maxCursor - this.cursor) - 1;
topChunk.set(bytes, chunkPos);
} else {
if (topChunk) {
const chunkPos = topChunk.length - (this.maxCursor - this.cursor) - 1;
if (chunkPos < topChunk.length) {
this.chunks[this.chunks.length - 1] = topChunk.subarray(0, chunkPos);
this.maxCursor = this.cursor - 1;
}
}
if (bytes.length < 64 && bytes.length < this.chunkSize) {
topChunk = alloc(this.chunkSize);
this.chunks.push(topChunk);
this.maxCursor += topChunk.length;
if (this._initReuseChunk === null) {
this._initReuseChunk = topChunk;
}
topChunk.set(bytes, 0);
} else {
this.chunks.push(bytes);
this.maxCursor += bytes.length;
}
}
this.cursor += bytes.length;
}
toBytes(reset = false) {
let byts;
if (this.chunks.length === 1) {
const chunk = this.chunks[0];
if (reset && this.cursor > chunk.length / 2) {
byts = this.cursor === chunk.length ? chunk : chunk.subarray(0, this.cursor);
this._initReuseChunk = null;
this.chunks = [];
} else {
byts = slice(chunk, 0, this.cursor);
}
} else {
byts = concat(this.chunks, this.cursor);
}
if (reset) {
this.reset();
}
return byts;
}
}
+228
View File
@@ -0,0 +1,228 @@
export const useBuffer = globalThis.process && !globalThis.process.browser && globalThis.Buffer && typeof globalThis.Buffer.isBuffer === 'function';
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
function isBuffer(buf) {
return useBuffer && globalThis.Buffer.isBuffer(buf);
}
export function asU8A(buf) {
if (!(buf instanceof Uint8Array)) {
return Uint8Array.from(buf);
}
return isBuffer(buf) ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : buf;
}
export const toString = useBuffer ? (bytes, start, end) => {
return end - start > 64 ? globalThis.Buffer.from(bytes.subarray(start, end)).toString('utf8') : utf8Slice(bytes, start, end);
} : (bytes, start, end) => {
return end - start > 64 ? textDecoder.decode(bytes.subarray(start, end)) : utf8Slice(bytes, start, end);
};
export const fromString = useBuffer ? string => {
return string.length > 64 ? globalThis.Buffer.from(string) : utf8ToBytes(string);
} : string => {
return string.length > 64 ? textEncoder.encode(string) : utf8ToBytes(string);
};
export const fromArray = arr => {
return Uint8Array.from(arr);
};
export const slice = useBuffer ? (bytes, start, end) => {
if (isBuffer(bytes)) {
return new Uint8Array(bytes.subarray(start, end));
}
return bytes.slice(start, end);
} : (bytes, start, end) => {
return bytes.slice(start, end);
};
export const concat = useBuffer ? (chunks, length) => {
chunks = chunks.map(c => c instanceof Uint8Array ? c : globalThis.Buffer.from(c));
return asU8A(globalThis.Buffer.concat(chunks, length));
} : (chunks, length) => {
const out = new Uint8Array(length);
let off = 0;
for (let b of chunks) {
if (off + b.length > out.length) {
b = b.subarray(0, out.length - off);
}
out.set(b, off);
off += b.length;
}
return out;
};
export const alloc = useBuffer ? size => {
return globalThis.Buffer.allocUnsafe(size);
} : size => {
return new Uint8Array(size);
};
export const toHex = useBuffer ? d => {
if (typeof d === 'string') {
return d;
}
return globalThis.Buffer.from(toBytes(d)).toString('hex');
} : d => {
if (typeof d === 'string') {
return d;
}
return Array.prototype.reduce.call(toBytes(d), (p, c) => `${ p }${ c.toString(16).padStart(2, '0') }`, '');
};
export const fromHex = useBuffer ? hex => {
if (hex instanceof Uint8Array) {
return hex;
}
return globalThis.Buffer.from(hex, 'hex');
} : hex => {
if (hex instanceof Uint8Array) {
return hex;
}
if (!hex.length) {
return new Uint8Array(0);
}
return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
};
function toBytes(obj) {
if (obj instanceof Uint8Array && obj.constructor.name === 'Uint8Array') {
return obj;
}
if (obj instanceof ArrayBuffer) {
return new Uint8Array(obj);
}
if (ArrayBuffer.isView(obj)) {
return new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength);
}
throw new Error('Unknown type, must be binary type');
}
export function compare(b1, b2) {
if (isBuffer(b1) && isBuffer(b2)) {
return b1.compare(b2);
}
for (let i = 0; i < b1.length; i++) {
if (b1[i] === b2[i]) {
continue;
}
return b1[i] < b2[i] ? -1 : 1;
}
return 0;
}
function utf8ToBytes(string, units = Infinity) {
let codePoint;
const length = string.length;
let leadSurrogate = null;
const bytes = [];
for (let i = 0; i < length; ++i) {
codePoint = string.charCodeAt(i);
if (codePoint > 55295 && codePoint < 57344) {
if (!leadSurrogate) {
if (codePoint > 56319) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
continue;
} else if (i + 1 === length) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
continue;
}
leadSurrogate = codePoint;
continue;
}
if (codePoint < 56320) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
leadSurrogate = codePoint;
continue;
}
codePoint = (leadSurrogate - 55296 << 10 | codePoint - 56320) + 65536;
} else if (leadSurrogate) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
}
leadSurrogate = null;
if (codePoint < 128) {
if ((units -= 1) < 0)
break;
bytes.push(codePoint);
} else if (codePoint < 2048) {
if ((units -= 2) < 0)
break;
bytes.push(codePoint >> 6 | 192, codePoint & 63 | 128);
} else if (codePoint < 65536) {
if ((units -= 3) < 0)
break;
bytes.push(codePoint >> 12 | 224, codePoint >> 6 & 63 | 128, codePoint & 63 | 128);
} else if (codePoint < 1114112) {
if ((units -= 4) < 0)
break;
bytes.push(codePoint >> 18 | 240, codePoint >> 12 & 63 | 128, codePoint >> 6 & 63 | 128, codePoint & 63 | 128);
} else {
throw new Error('Invalid code point');
}
}
return bytes;
}
function utf8Slice(buf, offset, end) {
const res = [];
while (offset < end) {
const firstByte = buf[offset];
let codePoint = null;
let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1;
if (offset + bytesPerSequence <= end) {
let secondByte, thirdByte, fourthByte, tempCodePoint;
switch (bytesPerSequence) {
case 1:
if (firstByte < 128) {
codePoint = firstByte;
}
break;
case 2:
secondByte = buf[offset + 1];
if ((secondByte & 192) === 128) {
tempCodePoint = (firstByte & 31) << 6 | secondByte & 63;
if (tempCodePoint > 127) {
codePoint = tempCodePoint;
}
}
break;
case 3:
secondByte = buf[offset + 1];
thirdByte = buf[offset + 2];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63;
if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) {
codePoint = tempCodePoint;
}
}
break;
case 4:
secondByte = buf[offset + 1];
thirdByte = buf[offset + 2];
fourthByte = buf[offset + 3];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63;
if (tempCodePoint > 65535 && tempCodePoint < 1114112) {
codePoint = tempCodePoint;
}
}
}
}
if (codePoint === null) {
codePoint = 65533;
bytesPerSequence = 1;
} else if (codePoint > 65535) {
codePoint -= 65536;
res.push(codePoint >>> 10 & 1023 | 55296);
codePoint = 56320 | codePoint & 1023;
}
res.push(codePoint);
offset += bytesPerSequence;
}
return decodeCodePointsArray(res);
}
const MAX_ARGUMENTS_LENGTH = 4096;
export function decodeCodePointsArray(codePoints) {
const len = codePoints.length;
if (len <= MAX_ARGUMENTS_LENGTH) {
return String.fromCharCode.apply(String, codePoints);
}
let res = '';
let i = 0;
while (i < len) {
res += String.fromCharCode.apply(String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH));
}
return res;
}
+19
View File
@@ -0,0 +1,19 @@
const decodeErrPrefix = 'CBOR decode error:';
const encodeErrPrefix = 'CBOR encode error:';
const uintMinorPrefixBytes = [];
uintMinorPrefixBytes[23] = 1;
uintMinorPrefixBytes[24] = 2;
uintMinorPrefixBytes[25] = 3;
uintMinorPrefixBytes[26] = 5;
uintMinorPrefixBytes[27] = 9;
function assertEnoughData(data, pos, need) {
if (data.length - pos < need) {
throw new Error(`${ decodeErrPrefix } not enough data for type`);
}
}
export {
decodeErrPrefix,
encodeErrPrefix,
uintMinorPrefixBytes,
assertEnoughData
};
+139
View File
@@ -0,0 +1,139 @@
import { decodeErrPrefix } from './common.js';
import { Type } from './token.js';
import {
jump,
quick
} from './jump.js';
const defaultDecodeOptions = {
strict: false,
allowIndefinite: true,
allowUndefined: true,
allowBigInt: true
};
class Tokeniser {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
}
done() {
return this.pos >= this.data.length;
}
next() {
const byt = this.data[this.pos];
let token = quick[byt];
if (token === undefined) {
const decoder = jump[byt];
if (!decoder) {
throw new Error(`${ decodeErrPrefix } no decoder for major type ${ byt >>> 5 } (byte 0x${ byt.toString(16).padStart(2, '0') })`);
}
const minor = byt & 31;
token = decoder(this.data, this.pos, minor, this.options);
}
this.pos += token.encodedLength;
return token;
}
}
const DONE = Symbol.for('DONE');
const BREAK = Symbol.for('BREAK');
function tokenToArray(token, tokeniser, options) {
const arr = [];
for (let i = 0; i < token.value; i++) {
const value = tokensToObject(tokeniser, options);
if (value === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ decodeErrPrefix } got unexpected break to lengthed array`);
}
if (value === DONE) {
throw new Error(`${ decodeErrPrefix } found array but not enough entries (got ${ i }, expected ${ token.value })`);
}
arr[i] = value;
}
return arr;
}
function tokenToMap(token, tokeniser, options) {
const useMaps = options.useMaps === true;
const obj = useMaps ? undefined : {};
const m = useMaps ? new Map() : undefined;
for (let i = 0; i < token.value; i++) {
const key = tokensToObject(tokeniser, options);
if (key === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ decodeErrPrefix } got unexpected break to lengthed map`);
}
if (key === DONE) {
throw new Error(`${ decodeErrPrefix } found map but not enough entries (got ${ i } [no key], expected ${ token.value })`);
}
if (useMaps !== true && typeof key !== 'string') {
throw new Error(`${ decodeErrPrefix } non-string keys not supported (got ${ typeof key })`);
}
if (options.rejectDuplicateMapKeys === true) {
if (useMaps && m.has(key) || !useMaps && key in obj) {
throw new Error(`${ decodeErrPrefix } found repeat map key "${ key }"`);
}
}
const value = tokensToObject(tokeniser, options);
if (value === DONE) {
throw new Error(`${ decodeErrPrefix } found map but not enough entries (got ${ i } [no value], expected ${ token.value })`);
}
if (useMaps) {
m.set(key, value);
} else {
obj[key] = value;
}
}
return useMaps ? m : obj;
}
function tokensToObject(tokeniser, options) {
if (tokeniser.done()) {
return DONE;
}
const token = tokeniser.next();
if (token.type === Type.break) {
return BREAK;
}
if (token.type.terminal) {
return token.value;
}
if (token.type === Type.array) {
return tokenToArray(token, tokeniser, options);
}
if (token.type === Type.map) {
return tokenToMap(token, tokeniser, options);
}
if (token.type === Type.tag) {
if (options.tags && typeof options.tags[token.value] === 'function') {
const tagged = tokensToObject(tokeniser, options);
return options.tags[token.value](tagged);
}
throw new Error(`${ decodeErrPrefix } tag not supported (${ token.value })`);
}
throw new Error('unsupported');
}
function decode(data, options) {
if (!(data instanceof Uint8Array)) {
throw new Error(`${ decodeErrPrefix } data to decode must be a Uint8Array`);
}
options = Object.assign({}, defaultDecodeOptions, options);
const tokeniser = options.tokenizer || new Tokeniser(data, options);
const decoded = tokensToObject(tokeniser, options);
if (decoded === DONE) {
throw new Error(`${ decodeErrPrefix } did not find any content to decode`);
}
if (decoded === BREAK) {
throw new Error(`${ decodeErrPrefix } got unexpected break`);
}
if (!tokeniser.done()) {
throw new Error(`${ decodeErrPrefix } too many terminals, data makes no sense`);
}
return decoded;
}
export {
Tokeniser,
tokensToObject,
decode
};
+123
View File
@@ -0,0 +1,123 @@
import { Tokeniser } from './decode.js';
import {
toHex,
fromHex
} from './byte-utils.js';
import { uintBoundaries } from './0uint.js';
const utf8Encoder = new TextEncoder();
const utf8Decoder = new TextDecoder();
function* tokensToDiagnostic(inp, width = 100) {
const tokeniser = new Tokeniser(inp, {
retainStringBytes: true,
allowBigInt: true
});
let pos = 0;
const indent = [];
const slc = (start, length) => {
return toHex(inp.slice(pos + start, pos + start + length));
};
while (!tokeniser.done()) {
const token = tokeniser.next();
let margin = ''.padStart(indent.length * 2, ' ');
let vLength = token.encodedLength - 1;
let v = String(token.value);
let outp = `${ margin }${ slc(0, 1) }`;
const str = token.type.name === 'bytes' || token.type.name === 'string';
if (token.type.name === 'string') {
v = v.length;
vLength -= v;
} else if (token.type.name === 'bytes') {
v = token.value.length;
vLength -= v;
}
let multilen;
switch (token.type.name) {
case 'string':
case 'bytes':
case 'map':
case 'array':
multilen = token.type.name === 'string' ? utf8Encoder.encode(token.value).length : token.value.length;
if (multilen >= uintBoundaries[0]) {
if (multilen < uintBoundaries[1]) {
outp += ` ${ slc(1, 1) }`;
} else if (multilen < uintBoundaries[2]) {
outp += ` ${ slc(1, 2) }`;
} else if (multilen < uintBoundaries[3]) {
outp += ` ${ slc(1, 4) }`;
} else if (multilen < uintBoundaries[4]) {
outp += ` ${ slc(1, 8) }`;
}
}
break;
default:
outp += ` ${ slc(1, vLength) }`;
break;
}
outp = outp.padEnd(width / 2, ' ');
outp += `# ${ margin }${ token.type.name }`;
if (token.type.name !== v) {
outp += `(${ v })`;
}
yield outp;
if (str) {
let asString = token.type.name === 'string';
margin += ' ';
let repr = asString ? utf8Encoder.encode(token.value) : token.value;
if (asString && token.byteValue !== undefined) {
if (repr.length !== token.byteValue.length) {
repr = token.byteValue;
asString = false;
}
}
const wh = (width / 2 - margin.length - 1) / 2;
let snip = 0;
while (repr.length - snip > 0) {
const piece = repr.slice(snip, snip + wh);
snip += piece.length;
const st = asString ? utf8Decoder.decode(piece) : piece.reduce((p, c) => {
if (c < 32 || c >= 127 && c < 161 || c === 173) {
return `${ p }\\x${ c.toString(16).padStart(2, '0') }`;
}
return `${ p }${ String.fromCharCode(c) }`;
}, '');
yield `${ margin }${ toHex(piece) }`.padEnd(width / 2, ' ') + `# ${ margin }"${ st }"`;
}
}
if (indent.length) {
indent[indent.length - 1]--;
}
if (!token.type.terminal) {
switch (token.type.name) {
case 'map':
indent.push(token.value * 2);
break;
case 'array':
indent.push(token.value);
break;
case 'tag':
indent.push(1);
break;
default:
throw new Error(`Unknown token type '${ token.type.name }'`);
}
}
while (indent.length && indent[indent.length - 1] <= 0) {
indent.pop();
}
pos += token.encodedLength;
}
}
function fromDiag(input) {
if (typeof input !== 'string') {
throw new TypeError('Expected string input');
}
input = input.replace(/#.*?$/mg, '').replace(/[\s\r\n]+/mg, '');
if (/[^a-f0-9]/i.test(input)) {
throw new TypeError('Input string was not CBOR diagnostic format');
}
return fromHex(input);
}
export {
tokensToDiagnostic,
fromDiag
};
+246
View File
@@ -0,0 +1,246 @@
import { is } from './is.js';
import {
Token,
Type
} from './token.js';
import { Bl } from './bl.js';
import { encodeErrPrefix } from './common.js';
import { quickEncodeToken } from './jump.js';
import { asU8A } from './byte-utils.js';
import { encodeUint } from './0uint.js';
import { encodeNegint } from './1negint.js';
import { encodeBytes } from './2bytes.js';
import { encodeString } from './3string.js';
import { encodeArray } from './4array.js';
import { encodeMap } from './5map.js';
import { encodeTag } from './6tag.js';
import { encodeFloat } from './7float.js';
const defaultEncodeOptions = {
float64: false,
mapSorter,
quickEncodeToken
};
export function makeCborEncoders() {
const encoders = [];
encoders[Type.uint.major] = encodeUint;
encoders[Type.negint.major] = encodeNegint;
encoders[Type.bytes.major] = encodeBytes;
encoders[Type.string.major] = encodeString;
encoders[Type.array.major] = encodeArray;
encoders[Type.map.major] = encodeMap;
encoders[Type.tag.major] = encodeTag;
encoders[Type.float.major] = encodeFloat;
return encoders;
}
const cborEncoders = makeCborEncoders();
const buf = new Bl();
class Ref {
constructor(obj, parent) {
this.obj = obj;
this.parent = parent;
}
includes(obj) {
let p = this;
do {
if (p.obj === obj) {
return true;
}
} while (p = p.parent);
return false;
}
static createCheck(stack, obj) {
if (stack && stack.includes(obj)) {
throw new Error(`${ encodeErrPrefix } object contains circular references`);
}
return new Ref(obj, stack);
}
}
const simpleTokens = {
null: new Token(Type.null, null),
undefined: new Token(Type.undefined, undefined),
true: new Token(Type.true, true),
false: new Token(Type.false, false),
emptyArray: new Token(Type.array, 0),
emptyMap: new Token(Type.map, 0)
};
const typeEncoders = {
number(obj, _typ, _options, _refStack) {
if (!Number.isInteger(obj) || !Number.isSafeInteger(obj)) {
return new Token(Type.float, obj);
} else if (obj >= 0) {
return new Token(Type.uint, obj);
} else {
return new Token(Type.negint, obj);
}
},
bigint(obj, _typ, _options, _refStack) {
if (obj >= BigInt(0)) {
return new Token(Type.uint, obj);
} else {
return new Token(Type.negint, obj);
}
},
Uint8Array(obj, _typ, _options, _refStack) {
return new Token(Type.bytes, obj);
},
string(obj, _typ, _options, _refStack) {
return new Token(Type.string, obj);
},
boolean(obj, _typ, _options, _refStack) {
return obj ? simpleTokens.true : simpleTokens.false;
},
null(_obj, _typ, _options, _refStack) {
return simpleTokens.null;
},
undefined(_obj, _typ, _options, _refStack) {
return simpleTokens.undefined;
},
ArrayBuffer(obj, _typ, _options, _refStack) {
return new Token(Type.bytes, new Uint8Array(obj));
},
DataView(obj, _typ, _options, _refStack) {
return new Token(Type.bytes, new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength));
},
Array(obj, _typ, options, refStack) {
if (!obj.length) {
if (options.addBreakTokens === true) {
return [
simpleTokens.emptyArray,
new Token(Type.break)
];
}
return simpleTokens.emptyArray;
}
refStack = Ref.createCheck(refStack, obj);
const entries = [];
let i = 0;
for (const e of obj) {
entries[i++] = objectToTokens(e, options, refStack);
}
if (options.addBreakTokens) {
return [
new Token(Type.array, obj.length),
entries,
new Token(Type.break)
];
}
return [
new Token(Type.array, obj.length),
entries
];
},
Object(obj, typ, options, refStack) {
const isMap = typ !== 'Object';
const keys = isMap ? obj.keys() : Object.keys(obj);
const length = isMap ? obj.size : keys.length;
if (!length) {
if (options.addBreakTokens === true) {
return [
simpleTokens.emptyMap,
new Token(Type.break)
];
}
return simpleTokens.emptyMap;
}
refStack = Ref.createCheck(refStack, obj);
const entries = [];
let i = 0;
for (const key of keys) {
entries[i++] = [
objectToTokens(key, options, refStack),
objectToTokens(isMap ? obj.get(key) : obj[key], options, refStack)
];
}
sortMapEntries(entries, options);
if (options.addBreakTokens) {
return [
new Token(Type.map, length),
entries,
new Token(Type.break)
];
}
return [
new Token(Type.map, length),
entries
];
}
};
typeEncoders.Map = typeEncoders.Object;
typeEncoders.Buffer = typeEncoders.Uint8Array;
for (const typ of 'Uint8Clamped Uint16 Uint32 Int8 Int16 Int32 BigUint64 BigInt64 Float32 Float64'.split(' ')) {
typeEncoders[`${ typ }Array`] = typeEncoders.DataView;
}
function objectToTokens(obj, options = {}, refStack) {
const typ = is(obj);
const customTypeEncoder = options && options.typeEncoders && options.typeEncoders[typ] || typeEncoders[typ];
if (typeof customTypeEncoder === 'function') {
const tokens = customTypeEncoder(obj, typ, options, refStack);
if (tokens != null) {
return tokens;
}
}
const typeEncoder = typeEncoders[typ];
if (!typeEncoder) {
throw new Error(`${ encodeErrPrefix } unsupported type: ${ typ }`);
}
return typeEncoder(obj, typ, options, refStack);
}
function sortMapEntries(entries, options) {
if (options.mapSorter) {
entries.sort(options.mapSorter);
}
}
function mapSorter(e1, e2) {
const keyToken1 = Array.isArray(e1[0]) ? e1[0][0] : e1[0];
const keyToken2 = Array.isArray(e2[0]) ? e2[0][0] : e2[0];
if (keyToken1.type !== keyToken2.type) {
return keyToken1.type.compare(keyToken2.type);
}
const major = keyToken1.type.major;
const tcmp = cborEncoders[major].compareTokens(keyToken1, keyToken2);
if (tcmp === 0) {
console.warn('WARNING: complex key types used, CBOR key sorting guarantees are gone');
}
return tcmp;
}
function tokensToEncoded(buf, tokens, encoders, options) {
if (Array.isArray(tokens)) {
for (const token of tokens) {
tokensToEncoded(buf, token, encoders, options);
}
} else {
encoders[tokens.type.major](buf, tokens, options);
}
}
function encodeCustom(data, encoders, options) {
const tokens = objectToTokens(data, options);
if (!Array.isArray(tokens) && options.quickEncodeToken) {
const quickBytes = options.quickEncodeToken(tokens);
if (quickBytes) {
return quickBytes;
}
const encoder = encoders[tokens.type.major];
if (encoder.encodedSize) {
const size = encoder.encodedSize(tokens, options);
const buf = new Bl(size);
encoder(buf, tokens, options);
if (buf.chunks.length !== 1) {
throw new Error(`Unexpected error: pre-calculated length for ${ tokens } was wrong`);
}
return asU8A(buf.chunks[0]);
}
}
buf.reset();
tokensToEncoded(buf, tokens, encoders, options);
return buf.toBytes(true);
}
function encode(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
return encodeCustom(data, cborEncoders, options);
}
export {
objectToTokens,
encode,
encodeCustom,
Ref
};
+81
View File
@@ -0,0 +1,81 @@
const typeofs = [
'string',
'number',
'bigint',
'symbol'
];
const objectTypeNames = [
'Function',
'Generator',
'AsyncGenerator',
'GeneratorFunction',
'AsyncGeneratorFunction',
'AsyncFunction',
'Observable',
'Array',
'Buffer',
'Object',
'RegExp',
'Date',
'Error',
'Map',
'Set',
'WeakMap',
'WeakSet',
'ArrayBuffer',
'SharedArrayBuffer',
'DataView',
'Promise',
'URL',
'HTMLElement',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'BigInt64Array',
'BigUint64Array'
];
export function is(value) {
if (value === null) {
return 'null';
}
if (value === undefined) {
return 'undefined';
}
if (value === true || value === false) {
return 'boolean';
}
const typeOf = typeof value;
if (typeofs.includes(typeOf)) {
return typeOf;
}
if (typeOf === 'function') {
return 'Function';
}
if (Array.isArray(value)) {
return 'Array';
}
if (isBuffer(value)) {
return 'Buffer';
}
const objectType = getObjectType(value);
if (objectType) {
return objectType;
}
return 'Object';
}
function isBuffer(value) {
return value && value.constructor && value.constructor.isBuffer && value.constructor.isBuffer.call(null, value);
}
function getObjectType(value) {
const objectTypeName = Object.prototype.toString.call(value).slice(8, -1);
if (objectTypeNames.includes(objectTypeName)) {
return objectTypeName;
}
return undefined;
}
+413
View File
@@ -0,0 +1,413 @@
import { decode as _decode } from '../decode.js';
import {
Token,
Type
} from '../token.js';
import { decodeCodePointsArray } from '../byte-utils.js';
import { decodeErrPrefix } from '../common.js';
class Tokenizer {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
this.modeStack = ['value'];
this.lastToken = '';
}
done() {
return this.pos >= this.data.length;
}
ch() {
return this.data[this.pos];
}
currentMode() {
return this.modeStack[this.modeStack.length - 1];
}
skipWhitespace() {
let c = this.ch();
while (c === 32 || c === 9 || c === 13 || c === 10) {
c = this.data[++this.pos];
}
}
expect(str) {
if (this.data.length - this.pos < str.length) {
throw new Error(`${ decodeErrPrefix } unexpected end of input at position ${ this.pos }`);
}
for (let i = 0; i < str.length; i++) {
if (this.data[this.pos++] !== str[i]) {
throw new Error(`${ decodeErrPrefix } unexpected token at position ${ this.pos }, expected to find '${ String.fromCharCode(...str) }'`);
}
}
}
parseNumber() {
const startPos = this.pos;
let negative = false;
let float = false;
const swallow = chars => {
while (!this.done()) {
const ch = this.ch();
if (chars.includes(ch)) {
this.pos++;
} else {
break;
}
}
};
if (this.ch() === 45) {
negative = true;
this.pos++;
}
if (this.ch() === 48) {
this.pos++;
if (this.ch() === 46) {
this.pos++;
float = true;
} else {
return new Token(Type.uint, 0, this.pos - startPos);
}
}
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
if (negative && this.pos === startPos + 1) {
throw new Error(`${ decodeErrPrefix } unexpected token at position ${ this.pos }`);
}
if (!this.done() && this.ch() === 46) {
if (float) {
throw new Error(`${ decodeErrPrefix } unexpected token at position ${ this.pos }`);
}
float = true;
this.pos++;
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
}
if (!this.done() && (this.ch() === 101 || this.ch() === 69)) {
float = true;
this.pos++;
if (!this.done() && (this.ch() === 43 || this.ch() === 45)) {
this.pos++;
}
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
}
const numStr = String.fromCharCode.apply(null, this.data.subarray(startPos, this.pos));
const num = parseFloat(numStr);
if (float) {
return new Token(Type.float, num, this.pos - startPos);
}
if (this.options.allowBigInt !== true || Number.isSafeInteger(num)) {
return new Token(num >= 0 ? Type.uint : Type.negint, num, this.pos - startPos);
}
return new Token(num >= 0 ? Type.uint : Type.negint, BigInt(numStr), this.pos - startPos);
}
parseString() {
if (this.ch() !== 34) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }; this shouldn't happen`);
}
this.pos++;
for (let i = this.pos, l = 0; i < this.data.length && l < 65536; i++, l++) {
const ch = this.data[i];
if (ch === 92 || ch < 32 || ch >= 128) {
break;
}
if (ch === 34) {
const str = String.fromCharCode.apply(null, this.data.subarray(this.pos, i));
this.pos = i + 1;
return new Token(Type.string, str, l);
}
}
const startPos = this.pos;
const chars = [];
const readu4 = () => {
if (this.pos + 4 >= this.data.length) {
throw new Error(`${ decodeErrPrefix } unexpected end of unicode escape sequence at position ${ this.pos }`);
}
let u4 = 0;
for (let i = 0; i < 4; i++) {
let ch = this.ch();
if (ch >= 48 && ch <= 57) {
ch -= 48;
} else if (ch >= 97 && ch <= 102) {
ch = ch - 97 + 10;
} else if (ch >= 65 && ch <= 70) {
ch = ch - 65 + 10;
} else {
throw new Error(`${ decodeErrPrefix } unexpected unicode escape character at position ${ this.pos }`);
}
u4 = u4 * 16 + ch;
this.pos++;
}
return u4;
};
const readUtf8Char = () => {
const firstByte = this.ch();
let codePoint = null;
let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1;
if (this.pos + bytesPerSequence > this.data.length) {
throw new Error(`${ decodeErrPrefix } unexpected unicode sequence at position ${ this.pos }`);
}
let secondByte, thirdByte, fourthByte, tempCodePoint;
switch (bytesPerSequence) {
case 1:
if (firstByte < 128) {
codePoint = firstByte;
}
break;
case 2:
secondByte = this.data[this.pos + 1];
if ((secondByte & 192) === 128) {
tempCodePoint = (firstByte & 31) << 6 | secondByte & 63;
if (tempCodePoint > 127) {
codePoint = tempCodePoint;
}
}
break;
case 3:
secondByte = this.data[this.pos + 1];
thirdByte = this.data[this.pos + 2];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63;
if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) {
codePoint = tempCodePoint;
}
}
break;
case 4:
secondByte = this.data[this.pos + 1];
thirdByte = this.data[this.pos + 2];
fourthByte = this.data[this.pos + 3];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63;
if (tempCodePoint > 65535 && tempCodePoint < 1114112) {
codePoint = tempCodePoint;
}
}
}
if (codePoint === null) {
codePoint = 65533;
bytesPerSequence = 1;
} else if (codePoint > 65535) {
codePoint -= 65536;
chars.push(codePoint >>> 10 & 1023 | 55296);
codePoint = 56320 | codePoint & 1023;
}
chars.push(codePoint);
this.pos += bytesPerSequence;
};
while (!this.done()) {
const ch = this.ch();
let ch1;
switch (ch) {
case 92:
this.pos++;
if (this.done()) {
throw new Error(`${ decodeErrPrefix } unexpected string termination at position ${ this.pos }`);
}
ch1 = this.ch();
this.pos++;
switch (ch1) {
case 34:
case 39:
case 92:
case 47:
chars.push(ch1);
break;
case 98:
chars.push(8);
break;
case 116:
chars.push(9);
break;
case 110:
chars.push(10);
break;
case 102:
chars.push(12);
break;
case 114:
chars.push(13);
break;
case 117:
chars.push(readu4());
break;
default:
throw new Error(`${ decodeErrPrefix } unexpected string escape character at position ${ this.pos }`);
}
break;
case 34:
this.pos++;
return new Token(Type.string, decodeCodePointsArray(chars), this.pos - startPos);
default:
if (ch < 32) {
throw new Error(`${ decodeErrPrefix } invalid control character at position ${ this.pos }`);
} else if (ch < 128) {
chars.push(ch);
this.pos++;
} else {
readUtf8Char();
}
}
}
throw new Error(`${ decodeErrPrefix } unexpected end of string at position ${ this.pos }`);
}
parseValue() {
switch (this.ch()) {
case 123:
this.modeStack.push('obj-start');
this.pos++;
return new Token(Type.map, Infinity, 1);
case 91:
this.modeStack.push('array-start');
this.pos++;
return new Token(Type.array, Infinity, 1);
case 34: {
return this.parseString();
}
case 110:
this.expect([
110,
117,
108,
108
]);
return new Token(Type.null, null, 4);
case 102:
this.expect([
102,
97,
108,
115,
101
]);
return new Token(Type.false, false, 5);
case 116:
this.expect([
116,
114,
117,
101
]);
return new Token(Type.true, true, 4);
case 45:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
return this.parseNumber();
default:
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }`);
}
}
next() {
this.skipWhitespace();
switch (this.currentMode()) {
case 'value':
this.modeStack.pop();
return this.parseValue();
case 'array-value': {
this.modeStack.pop();
if (this.ch() === 93) {
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
if (this.ch() !== 44) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting array delimiter but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.modeStack.push('array-value');
this.skipWhitespace();
return this.parseValue();
}
case 'array-start': {
this.modeStack.pop();
if (this.ch() === 93) {
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
this.modeStack.push('array-value');
this.skipWhitespace();
return this.parseValue();
}
case 'obj-key':
if (this.ch() === 125) {
this.modeStack.pop();
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
if (this.ch() !== 44) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting object delimiter but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.skipWhitespace();
case 'obj-start': {
this.modeStack.pop();
if (this.ch() === 125) {
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
const token = this.parseString();
this.skipWhitespace();
if (this.ch() !== 58) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting key/value delimiter ':' but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.modeStack.push('obj-value');
return token;
}
case 'obj-value': {
this.modeStack.pop();
this.modeStack.push('obj-key');
this.skipWhitespace();
return this.parseValue();
}
default:
throw new Error(`${ decodeErrPrefix } unexpected parse state at position ${ this.pos }; this shouldn't happen`);
}
}
}
function decode(data, options) {
options = Object.assign({ tokenizer: new Tokenizer(data, options) }, options);
return _decode(data, options);
}
export {
decode,
Tokenizer
};

Some files were not shown because too many files have changed in this diff Show More