├── test ├── data │ ├── integer.cbor │ ├── map.cbor │ ├── floats.cbor │ ├── tagged_date.cbor │ ├── indef_string.cbor │ └── nested_array.cbor ├── .gitignore ├── issues │ ├── issue62 │ │ ├── raw.txt │ │ ├── issue62.dart │ │ ├── decoded.txt │ │ └── jsonconverted.txt │ ├── issue41_test.dart │ ├── issue18_test.dart │ ├── issue75 │ │ ├── issue75.dart │ │ └── long-decode-file.txt │ ├── issue9_test.dart │ └── issue51_test.dart ├── datetime_test.dart ├── int_test.dart ├── rational_number_test.dart ├── json_encoder_test.dart ├── filebased_test.dart ├── supplementary_test.dart └── simple_encoder_rfc_test.dart ├── example ├── example.md ├── encode.dart └── stream_decode.dart ├── sbom.yaml ├── AUTHORS ├── .gitignore ├── lib ├── simple.dart ├── src │ ├── error.dart │ ├── codec.dart │ ├── constants.dart │ ├── decoder │ │ ├── stage2.dart │ │ ├── stage0.dart │ │ ├── decoder.dart │ │ ├── pretty_print.dart │ │ └── stage1.dart │ ├── encoder │ │ ├── encoder.dart │ │ └── sink.dart │ ├── json.dart │ ├── utils │ │ ├── arg.dart │ │ └── utils.dart │ ├── value │ │ ├── internal.dart │ │ ├── simple_value.dart │ │ ├── int.dart │ │ ├── map.dart │ │ ├── double.dart │ │ ├── bytes.dart │ │ ├── value.dart │ │ ├── list.dart │ │ └── string.dart │ └── simple.dart └── cbor.dart ├── pubspec.yaml ├── analysis_options.yaml ├── .github └── workflows │ ├── ci.yml │ └── pana.yml ├── LICENSE ├── CHANGELOG.md ├── README.md └── sbom.spdx /test/data/integer.cbor: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | data/*.out 2 | -------------------------------------------------------------------------------- /test/data/map.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shamblett/cbor/HEAD/test/data/map.cbor -------------------------------------------------------------------------------- /test/data/floats.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shamblett/cbor/HEAD/test/data/floats.cbor -------------------------------------------------------------------------------- /test/data/tagged_date.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shamblett/cbor/HEAD/test/data/tagged_date.cbor -------------------------------------------------------------------------------- /test/data/indef_string.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shamblett/cbor/HEAD/test/data/indef_string.cbor -------------------------------------------------------------------------------- /test/data/nested_array.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shamblett/cbor/HEAD/test/data/nested_array.cbor -------------------------------------------------------------------------------- /test/issues/issue62/raw.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shamblett/cbor/HEAD/test/issues/issue62/raw.txt -------------------------------------------------------------------------------- /example/example.md: -------------------------------------------------------------------------------- 1 | ## Please refer to the example Dart files in this directory and the README for usage examples. -------------------------------------------------------------------------------- /sbom.yaml: -------------------------------------------------------------------------------- 1 | type: spdx 2 | 3 | spdx: 4 | SPDXFormat: 'tagvalue' 5 | documentCreation: 6 | CreatorComment: 'Please refer to the AUTHORS file for contributor/creation details' -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # A non-exhaustive list of contributors to the Dart CBOR package. 2 | # Names to be added alphabetically. 3 | # 4 | # For a complete list, see the git revision history. 5 | 6 | N. Nattis 7 | S. Hamblett 8 | -------------------------------------------------------------------------------- /test/issues/issue41_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 02/04/2020 5 | * Copyright : S.Hamblett 6 | */ 7 | import 'package:cbor/cbor.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | void main() { 11 | test('63254', () { 12 | final decoded = cbor.decode([25, 247, 22]); 13 | 14 | expect(decoded, CborSmallInt(63254)); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .packages 3 | .pub/ 4 | build/ 5 | 6 | # Remove the following pattern if you wish to check in your lock file 7 | pubspec.lock 8 | 9 | # Files created by dart2js 10 | *.part.js 11 | *.js.deps 12 | *.info.json 13 | 14 | # Directory created by dartdoc 15 | doc/api/ 16 | 17 | # JetBrains IDEs 18 | .idea/ 19 | *.ipr 20 | *.iws 21 | 22 | # Dart 2 additions 23 | .dart_tool/ 24 | -------------------------------------------------------------------------------- /lib/simple.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | /// This API exposes [CborSimpleCodec], which allows converting from CBOR 9 | /// to Dart objects without `CborValue`. 10 | /// 11 | /// Everything this API exposes, except the [cbor] constant, is exposed 12 | /// from the advanced API too. 13 | library; 14 | 15 | import 'src/simple.dart'; 16 | 17 | export 'src/simple.dart'; 18 | 19 | /// A constant instance of [CborSimpleCodec]. 20 | const CborSimpleCodec cbor = CborSimpleCodec(); 21 | -------------------------------------------------------------------------------- /test/issues/issue18_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 02/04/2020 5 | * Copyright : S.Hamblett 6 | */ 7 | import 'package:cbor/cbor.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | void main() { 11 | test('Uint8List in array', () { 12 | final encoded = cbor.encode( 13 | CborMap({ 14 | CborString('data'): CborList([ 15 | CborBytes([196, 79, 51]), 16 | ]), 17 | }), 18 | ); 19 | expect(encoded, [161, 100, 100, 97, 116, 97, 129, 67, 196, 79, 51]); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cbor 2 | description: A CBOR library for Dart. An RFC8949 compliant encoding/decoding CBOR implementation. 3 | version: 6.3.7 4 | repository: https://github.com/shamblett/cbor 5 | homepage: https://github.com/shamblett/cbor 6 | 7 | funding: 8 | - https://www.darticulate.com/#funding 9 | 10 | environment: 11 | sdk: '>=3.7.0 <4.0.0' 12 | 13 | dependencies: 14 | collection: '^1.17.2' 15 | typed_data: '^1.3.2' 16 | hex: '^0.2.0' 17 | convert: '^3.1.2' 18 | ieee754: '^1.0.3' 19 | meta: '^1.9.1' 20 | characters: '^1.4.0' 21 | 22 | dev_dependencies: 23 | test: '^1.25.14' 24 | lints: '^5.1.1' 25 | dart_code_metrics_presets: '^2.21.0' 26 | -------------------------------------------------------------------------------- /test/issues/issue62/issue62.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 02/04/2020 5 | * Copyright : S.Hamblett 6 | */ 7 | import 'dart:io'; 8 | 9 | import 'package:cbor/cbor.dart'; 10 | import 'package:test/test.dart'; 11 | 12 | void main() { 13 | test('Weird JSON conversion', () async { 14 | final currDir = Directory.current.path; 15 | final f = File('$currDir/test/issue62/raw.txt'); 16 | final decoded = await f.openRead().transform(cbor.decoder).single; 17 | expect(decoded.toString().isNotEmpty, isTrue); 18 | final jsonEncoder = CborJsonEncoder(); 19 | final json = jsonEncoder.convert(decoded); 20 | print(json); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file allows you to configure the Dart analyzer. 2 | # 3 | # The commented part below is just for inspiration. Read the guide here: 4 | # https://www.dartlang.org/guides/language/analysis-options 5 | include: package:lints/recommended.yaml 6 | 7 | analyzer: 8 | exclude: 9 | 10 | dart_code_metrics: 11 | exclude: 12 | metrics: 13 | - test/** 14 | - example/** 15 | rules: 16 | - test/** 17 | - example/** 18 | extends: 19 | - package:dart_code_metrics_presets/dart_all.yaml 20 | rules: 21 | - avoid-dynamic : false 22 | - avoid-non-null-assertion : false 23 | - newline-before-return : false 24 | - avoid-late-keyword : false 25 | - prefer-match-file-name : false 26 | -------------------------------------------------------------------------------- /lib/src/error.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | /// An exception raised when decoding. 9 | class CborMalformedException extends FormatException { 10 | const CborMalformedException(super.message, [int? super.offset]); 11 | 12 | @override 13 | String toString() { 14 | var string = 'Malformed CBOR'; 15 | if (offset != null) { 16 | string += ' at $offset'; 17 | } 18 | string += ': $message'; 19 | 20 | return string; 21 | } 22 | } 23 | 24 | /// Raised when object could not be codified due to cyclic references. 25 | class CborCyclicError extends Error { 26 | final Object cause; 27 | 28 | CborCyclicError(this.cause); 29 | 30 | @override 31 | String toString() { 32 | return 'Cbor cyclic error'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build Status 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: dart-lang/setup-dart@v1 16 | 17 | - name: Install dependencies 18 | run: dart pub get 19 | 20 | - name: Verify formatting 21 | run: dart format --output=none --set-exit-if-changed . 22 | 23 | - name: Analyze project source 24 | run: dart analyze 25 | 26 | - name: Run tests with coverage 27 | run: dart run coverage:test_with_coverage 28 | 29 | - name: Upload to codecov.io 30 | uses: codecov/codecov-action@v2 31 | with: 32 | token: ${{ secrets.CODECOV_TOKEN }} 33 | file: coverage/lcov.info 34 | name: Upload to codecov.io 35 | verbose: true 36 | -------------------------------------------------------------------------------- /example/encode.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | 10 | /// A simple encoding sequence followed by a self decode and a pretty print. 11 | int main() { 12 | final value = CborValue([ 13 | [1, 2, 3], // Writes an array 14 | CborBytes([0x00]), // Writes a byte string 15 | 67.89, 16 | 10, 17 | // You can encode maps with any value encodable as CBOR as key. 18 | {1: 'one', 2: 'two'}, 19 | 'hello', 20 | 21 | // Indefinite length string 22 | CborEncodeIndefiniteLengthString(['hello', ' ', 'world']), 23 | ]); 24 | 25 | final bytes = cbor.encode(value); 26 | 27 | final _ = cbor.decode(bytes); 28 | 29 | // Pretty print 30 | print(cborPrettyPrint(bytes)); 31 | 32 | // Print json 33 | print(const CborJsonEncoder().convert(value)); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /test/issues/issue75/issue75.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 03/05/2025 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | import 'dart:io'; 10 | 11 | import 'package:cbor/cbor.dart'; 12 | import 'package:test/test.dart'; 13 | 14 | void main() { 15 | test('Decode time', () { 16 | final currDir = Directory.current.path; 17 | final testFile = File('$currDir/test/issues/issue75/long-decode-file.txt'); 18 | final contents = testFile.readAsStringSync(); 19 | final toDecode = base64Decode(contents); 20 | 21 | final start = DateTime.now(); 22 | final decoded = cbor.decode(toDecode); 23 | final end = DateTime.now(); 24 | 25 | final timing = end.difference(start); 26 | print('Time taken : ${timing.inMilliseconds} ms'); 27 | final jsonEncoder = CborJsonEncoder(); 28 | final json = jsonEncoder.convert(decoded); 29 | print(json); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/codec.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | 12 | /// A constant instance of a CBOR codec, using default parameters. 13 | const CborCodec cbor = CborCodec(); 14 | 15 | /// A CBOR encoder and decoder. 16 | /// 17 | /// For exceptions [decode] may throw, see [CborDecoder]. 18 | class CborCodec extends Codec> { 19 | @override 20 | final CborDecoder decoder = const CborDecoder(); 21 | @override 22 | final CborEncoder encoder = const CborEncoder(); 23 | 24 | /// Create a CBOR codec. 25 | const CborCodec(); 26 | } 27 | 28 | /// Alias for `cbor.encode`. 29 | List cborEncode(CborValue value) { 30 | return cbor.encode(value); 31 | } 32 | 33 | /// Alias for `cbor.decode`. 34 | CborValue cborDecode(List value) { 35 | return cbor.decode(value); 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/pana.yml: -------------------------------------------------------------------------------- 1 | name: Pana Analysis 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - development 7 | 8 | jobs: 9 | 10 | package-analysis: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - uses: axel-op/dart-package-analyzer@v3 18 | id: analysis # set an id for the current step 19 | with: 20 | githubToken: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | # You can then use this id to retrieve the outputs in the next steps. 23 | # The following step shows how to exit the workflow with an error if a score is below 100: 24 | - name: Check scores 25 | env: 26 | # NB: "analysis" is the id set above. Replace it with the one you used if different. 27 | TOTAL: ${{ steps.analysis.outputs.total }} 28 | TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} 29 | run: | 30 | PERCENTAGE=$(( $TOTAL * 100 / $TOTAL_MAX )) 31 | if (( $PERCENTAGE < 50 )) 32 | then 33 | echo Score too low! 34 | exit 1 35 | fi -------------------------------------------------------------------------------- /lib/src/constants.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 27/03/2025 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | // dart format width=123 9 | 10 | /// Constants 11 | class CborConstants { 12 | static const prettyPrintIndent = 2; 13 | static const hexRadix = 16; 14 | static const binRadix = 2; 15 | static const bytesPerWord = 4; 16 | static const bitsPerWord = 32; 17 | static const bitsPerDoubleWord = 64; 18 | static const additionalInfoByteRange = 5; 19 | static const additionalInfoBitMask = 0x1f; 20 | static const byteLength = 8; 21 | static const bigIntSlice = 33; 22 | static const maxYear = 9999; 23 | static const seconds = 60; 24 | static const milliseconds = 1000; 25 | static const jsonBitLength = 53; 26 | 27 | // Bitfields, padding, indents and ranges 28 | static const one = 1; 29 | static const two = 2; 30 | static const three = 3; 31 | static const four = 4; 32 | static const five = 5; 33 | static const six = 6; 34 | static const seven = 7; 35 | static const eight = 8; 36 | static const nine = 9; 37 | static const sixteen = 16; 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Steve Hamblett 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /example/stream_decode.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | 10 | /// A stream based decode. 11 | Future main() async { 12 | // Assume we have received a CBOR encoded byte buffer from the network. 13 | // The byte sequence below gives :- 14 | // {'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E'} 15 | final payload = Stream.fromIterable( 16 | [ 17 | 0xa5, 18 | 0x61, 19 | 0x61, 20 | 0x61, 21 | 0x41, 22 | 0x61, 23 | 0x62, 24 | 0x61, 25 | 0x42, 26 | 0x61, 27 | 0x63, 28 | 0x61, 29 | 0x43, 30 | 0x61, 31 | 0x64, 32 | 0x61, 33 | 0x44, 34 | 0x61, 35 | 0x65, 36 | 0x61, 37 | 0x45, 38 | ].map((x) => [x]), 39 | ); 40 | 41 | // Decode using stream transforming 42 | // We use single because this is a single item 43 | final decoded = await payload.transform(cbor.decoder).single; 44 | 45 | // Print preview of decoded item 46 | // Should print {a: A, b: B, c: C, d: D, e: E} 47 | print(decoded); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /test/datetime_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:test/test.dart'; 10 | 11 | void main() { 12 | group('DateTime conversion tests', () { 13 | test('With 0 ms', () { 14 | expect( 15 | CborDateTimeString(DateTime.utc(2013, 3, 21, 20, 4, 0)).toString(), 16 | '2013-03-21T20:04:00Z', 17 | ); 18 | }); 19 | 20 | test('With 200 ms', () { 21 | expect( 22 | CborDateTimeString(DateTime.utc(2013, 3, 21, 20, 4, 0, 200)).toString(), 23 | '2013-03-21T20:04:00.2Z', 24 | ); 25 | }); 26 | 27 | test('With 200.5 ms', () { 28 | expect( 29 | CborDateTimeString( 30 | DateTime.utc(2013, 3, 21, 20, 4, 0, 200, 500), 31 | ).toString(), 32 | '2013-03-21T20:04:00.2005Z', 33 | ); 34 | }); 35 | 36 | test('With timezone', () { 37 | expect( 38 | CborDateTimeString( 39 | DateTime(2013, 3, 21, 20, 4, 0), 40 | timeZoneOffset: Duration(hours: 2, minutes: 30), 41 | ).toString(), 42 | '2013-03-21T20:04:00+02:30', 43 | ); 44 | }); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/decoder/stage2.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | 10 | import 'stage1.dart'; 11 | 12 | class RawValueTagged { 13 | final Header header; 14 | final List data; 15 | final int offset; 16 | final List tags; 17 | 18 | RawValueTagged( 19 | this.header, { 20 | this.data = const [], 21 | required this.offset, 22 | this.tags = const [], 23 | }); 24 | } 25 | 26 | class RawSinkTagged implements Sink { 27 | final Sink sink; 28 | List _tags = []; 29 | 30 | RawSinkTagged(this.sink); 31 | 32 | @override 33 | void add(RawValue data) { 34 | if (data.header.majorType == CborMajorType.tag) { 35 | if (data.header.arg.isIndefiniteLength) { 36 | throw CborMalformedException('Tag can not have additional info 31'); 37 | } 38 | 39 | _tags.add(data.header.arg.toInt()); 40 | } else { 41 | sink.add( 42 | RawValueTagged( 43 | data.header, 44 | offset: data.start, 45 | data: data.data, 46 | tags: _clearTags(), 47 | ), 48 | ); 49 | } 50 | } 51 | 52 | @override 53 | void close() { 54 | sink.close(); 55 | } 56 | 57 | List _clearTags() { 58 | final tags = _tags; 59 | _tags = []; 60 | return tags; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/encoder/encoder.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:typed_data/typed_buffers.dart'; 12 | 13 | import 'sink.dart'; 14 | 15 | /// A CBOR encoder. 16 | /// 17 | /// If cyclic references are found while encoding, a [CborCyclicError] will 18 | /// be thrown. 19 | /// 20 | /// Other than that, usage of the encoder is not expected to produce exceptions. 21 | /// 22 | /// This encoder supports [startChunkedConversion] and can therefore be 23 | /// used as a stream transformer. 24 | class CborEncoder extends Converter> { 25 | const CborEncoder(); 26 | 27 | @override 28 | List convert(CborValue input) { 29 | final b = Uint8Buffer(); 30 | input.encode(EncodeSink.withBuffer(b)); 31 | return b; 32 | } 33 | 34 | @override 35 | Sink startChunkedConversion(Sink> sink) { 36 | return _ChunkedConversion(EncodeSink.withSink(sink)); 37 | } 38 | } 39 | 40 | class _ChunkedConversion implements Sink { 41 | final EncodeSink sink; 42 | 43 | _ChunkedConversion(this.sink); 44 | 45 | @override 46 | void add(CborValue data) { 47 | data.encode(sink); 48 | } 49 | 50 | @override 51 | void close() { 52 | sink.close(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/decoder/stage0.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:math'; 9 | import 'dart:typed_data'; 10 | 11 | import 'package:typed_data/typed_buffers.dart' as typed; 12 | 13 | import '../constants.dart'; 14 | 15 | class Reader { 16 | final typed.Uint8Buffer _bytes = typed.Uint8Buffer(); 17 | int _read = 0; 18 | int _offset = 0; 19 | 20 | int get offset => _offset; 21 | int get length => _bytes.length - _read; 22 | 23 | void add(List input, [int start = 0, int? end]) => 24 | _bytes.addAll(input, start, end); 25 | 26 | int? peekUint8() { 27 | if (_read == _bytes.length) { 28 | return null; 29 | } 30 | 31 | return _bytes[_read]; 32 | } 33 | 34 | int? readUint8() { 35 | return readExactBytes(1)?.first; 36 | } 37 | 38 | Uint8List? readExactBytes(int c) { 39 | if (_read + c <= _bytes.length) { 40 | final bytes = _bytes.buffer.asUint8List().sublist(_read, c + _read); 41 | _read += c; 42 | _offset += c; 43 | 44 | if (length < _bytes.length ~/ CborConstants.bytesPerWord) { 45 | _bytes.removeRange(0, _read); 46 | _read = 0; 47 | } 48 | 49 | return bytes; 50 | } 51 | return null; 52 | } 53 | 54 | Uint8List readBytes(int maximum) { 55 | return readExactBytes(min(maximum, length))!; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/int_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : Alex Dochioiu 4 | * Date : 19/06/2024 5 | * Copyright : Alex Dochioiu 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:test/test.dart'; 10 | 11 | void main() { 12 | group("Int Numbers Test", () { 13 | final numbersToTest = [ 14 | BigInt.parse("0"), 15 | BigInt.parse("1"), 16 | BigInt.parse("2656159029"), 17 | BigInt.parse("4294967295"), 18 | BigInt.parse("4294967296"), 19 | BigInt.parse("4294967299"), 20 | BigInt.parse("5294967590"), 21 | BigInt.parse("91234567890"), 22 | BigInt.parse("912345678901234567890"), 23 | ]; 24 | 25 | group("positive numbers", () { 26 | for (final number in numbersToTest) { 27 | test(number.toString(), () { 28 | final cborInt = CborInt(number); 29 | final serialized = cborEncode(cborInt); 30 | final deserialized = cborDecode(serialized); 31 | expect(cborInt.toBigInt(), number); 32 | expect(deserialized, isA()); 33 | expect((deserialized as CborInt).toBigInt(), number); 34 | }); 35 | } 36 | }); 37 | 38 | group("negative numbers", () { 39 | for (final positiveNumber in numbersToTest) { 40 | final number = BigInt.zero - positiveNumber; 41 | test(number.toString(), () { 42 | final cborInt = CborInt(number); 43 | final serialized = cborEncode(cborInt); 44 | final deserialized = cborDecode(serialized); 45 | expect(cborInt.toBigInt(), number); 46 | expect(deserialized, isA()); 47 | expect((deserialized as CborInt).toBigInt(), number); 48 | }); 49 | } 50 | }); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/json.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | 12 | import 'utils/utils.dart'; 13 | 14 | /// Encodes CBOR into JSON. 15 | /// 16 | /// If the keys for a map are not strings, they are encoded recursively 17 | /// as JSON, and the string is used. 18 | /// 19 | /// This encoder supports [startChunkedConversion] and can therefore be 20 | /// used as a stream transformer. 21 | class CborJsonEncoder extends Converter { 22 | final Object? _substituteValue; 23 | final bool _allowMalformedUtf8; 24 | 25 | /// Create a new CBOR JSON encoder. 26 | /// 27 | /// [substituteValue] will be used for values that cannot be encoded, such 28 | /// as [double.infinity], [double.nan], [CborUndefined]. By default this 29 | /// is `null`. 30 | /// 31 | /// If [allowMalformedUtf8] is `false`, the decoder will 32 | /// throw [FormatException] when invalid UTF-8 is found. Otherwise, 33 | /// it will use replacement characters. 34 | const CborJsonEncoder({ 35 | Object? substituteValue, 36 | bool allowMalformedUtf8 = false, 37 | }) : _allowMalformedUtf8 = allowMalformedUtf8, 38 | _substituteValue = substituteValue; 39 | 40 | @override 41 | String convert(CborValue input) { 42 | return json.encode( 43 | input.toJson( 44 | substituteValue: _substituteValue, 45 | allowMalformedUtf8: _allowMalformedUtf8, 46 | ), 47 | ); 48 | } 49 | 50 | @override 51 | Sink startChunkedConversion(Sink sink) { 52 | return const JsonEncoder() 53 | .startChunkedConversion(sink) 54 | .map( 55 | (x) => x.toJson( 56 | substituteValue: _substituteValue, 57 | allowMalformedUtf8: _allowMalformedUtf8, 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/utils/arg.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import '../constants.dart'; 9 | 10 | /// The information encoded by additional Arg and the following bytes. 11 | abstract class Arg { 12 | static const Arg indefiniteLength = _ArgIndefiniteLength(); 13 | 14 | bool get isIndefiniteLength; 15 | 16 | bool get isValidInt; 17 | 18 | const factory Arg.int(int arg) = _ArgInt; 19 | 20 | factory Arg.bigInt(BigInt arg) = _ArgBigInt; 21 | 22 | Arg operator ~(); 23 | 24 | int toInt(); 25 | BigInt toBigInt(); 26 | } 27 | 28 | class _ArgIndefiniteLength implements Arg { 29 | @override 30 | final bool isIndefiniteLength = true; 31 | 32 | @override 33 | final bool isValidInt = false; 34 | 35 | const _ArgIndefiniteLength(); 36 | 37 | @override 38 | _ArgIndefiniteLength operator ~() => this; 39 | 40 | @override 41 | int toInt() => 0; 42 | @override 43 | BigInt toBigInt() => BigInt.zero; 44 | } 45 | 46 | class _ArgInt implements Arg { 47 | final int value; 48 | 49 | @override 50 | final bool isIndefiniteLength = false; 51 | 52 | @override 53 | final bool isValidInt = true; 54 | 55 | const _ArgInt(this.value); 56 | 57 | @override 58 | _ArgInt operator ~() => _ArgInt( 59 | (~BigInt.from(value)).toSigned(CborConstants.bigIntSlice).toInt(), 60 | ); 61 | 62 | @override 63 | int toInt() => value; 64 | @override 65 | BigInt toBigInt() => BigInt.from(value); 66 | } 67 | 68 | class _ArgBigInt implements Arg { 69 | final BigInt value; 70 | 71 | @override 72 | final bool isIndefiniteLength = false; 73 | 74 | @override 75 | final bool isValidInt = false; 76 | 77 | _ArgBigInt(this.value); 78 | 79 | @override 80 | _ArgBigInt operator ~() => _ArgBigInt(~value); 81 | 82 | @override 83 | int toInt() => value.toInt(); 84 | @override 85 | BigInt toBigInt() => value; 86 | } 87 | -------------------------------------------------------------------------------- /lib/cbor.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | /// Advanced API for CBOR. 9 | /// 10 | /// # Quick reference 11 | /// 12 | /// ## Decoders and encoders 13 | /// 14 | /// Between bytes and [CborValue]: 15 | /// 16 | /// * [CborCodec] 17 | /// * [CborDecoder] 18 | /// * [CborEncoder] 19 | /// * [cbor] 20 | /// * [cborDecode] 21 | /// * [cborEncode] 22 | /// 23 | /// Between bytes and objects: 24 | /// 25 | /// * [CborSimpleCodec] 26 | /// * [CborSimpleDecoder] 27 | /// * [CborSimpleEncoder] 28 | /// 29 | /// To JSON from [CborValue]: 30 | /// 31 | /// * [CborJsonEncoder] 32 | /// 33 | /// ## Pretty printing 34 | /// 35 | /// * [cborPrettyPrint] 36 | /// 37 | /// ## Values 38 | /// 39 | /// Super type: 40 | /// 41 | /// * [CborValue] 42 | /// 43 | /// Interfaces: 44 | /// 45 | /// * [CborDateTime] 46 | /// * Implementers: 47 | /// * [CborDateTimeString] 48 | /// * [CborDateTimeInt] 49 | /// * [CborDateTimeFloat] 50 | /// * [CborInt] 51 | /// * Implementers: 52 | /// * [CborSmallInt] 53 | /// * [CborBigInt] 54 | /// * [CborDateTimeInt] 55 | /// 56 | /// Major types: 57 | /// 58 | /// * [CborBytes] 59 | /// * [CborString] 60 | /// * [CborFloat] 61 | /// * [CborMap] 62 | /// * [CborList] 63 | /// * [CborInt] 64 | /// * [CborSmallInt] 65 | /// * [CborSimpleValue] 66 | /// * [CborNull] 67 | /// * [CborBool] 68 | /// * [CborUndefined] 69 | /// 70 | /// Tag based: 71 | /// 72 | /// * [CborDateTimeInt] 73 | /// * [CborBigInt] 74 | /// * [CborDateTimeString] 75 | /// * [CborBase64] 76 | /// * [CborBase64Url] 77 | /// * [CborMime] 78 | /// * [CborRegex] 79 | /// * [CborDateTimeFloat] 80 | /// * [CborDecimalFraction] 81 | /// * [CborRationalNumber] 82 | /// * [CborBigFloat] 83 | library; 84 | 85 | export 'src/codec.dart'; 86 | export 'src/decoder/decoder.dart'; 87 | export 'src/decoder/pretty_print.dart'; 88 | export 'src/encoder/encoder.dart'; 89 | export 'src/error.dart'; 90 | export 'src/json.dart'; 91 | export 'src/simple.dart'; 92 | export 'src/value/value.dart'; 93 | -------------------------------------------------------------------------------- /lib/src/decoder/decoder.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | // The decoder works with 3 stages 9 | // 10 | // Stage 0: not really decoder stage, utils used for handling bytes. 11 | // 12 | // Stage 1: sort of a lexer, transforms the input into values with 13 | // header and data. a list will be a single item followed by its items. 14 | // 15 | // Stage 2: associate tags with values. 16 | // 17 | // Stage 3: produce final values, using builders that take values until 18 | // it is done. a list builder will be created when a list header is found 19 | // and then will keep taking values until the list is full. 20 | 21 | import 'dart:convert'; 22 | 23 | import 'package:cbor/cbor.dart'; 24 | 25 | import 'stage1.dart'; 26 | import 'stage2.dart'; 27 | import 'stage3.dart'; 28 | 29 | /// A CBOR decoder. 30 | /// 31 | /// If the input is malformed, the decoder will throw [CborMalformedException]. 32 | /// 33 | /// The decoder will not throw if the input is invalid but well-formed. 34 | /// However, operations on [CborValue] may throw if invalid data is used. 35 | /// 36 | /// This decoder supports [startChunkedConversion] and can therefore be 37 | /// used as a stream transformer. The decoder operates over a CBOR sequence 38 | /// in this mode. 39 | class CborDecoder extends Converter, CborValue> { 40 | /// Create a CBOR decoder. 41 | const CborDecoder(); 42 | 43 | /// Converts [input] and returns the result of the conversion. 44 | /// 45 | /// If the input does not contain a single CBOR item, [FormatException] will 46 | /// be thrown. 47 | @override 48 | CborValue convert(List input) { 49 | CborValue? value; 50 | 51 | startChunkedConversion( 52 | ChunkedConversionSink.withCallback((values) { 53 | if (values.isEmpty) { 54 | throw FormatException('Expected at least one CBOR value.'); 55 | } 56 | if (values.length > 1) { 57 | throw FormatException('Expected at most one CBOR value.'); 58 | } 59 | 60 | value = values.single; 61 | }), 62 | ) 63 | ..add(input) 64 | ..close(); 65 | 66 | return value!; 67 | } 68 | 69 | @override 70 | ByteConversionSink startChunkedConversion(Sink sink) => 71 | RawSink(RawSinkTagged(CborSink(sink))); 72 | } 73 | -------------------------------------------------------------------------------- /test/issues/issue9_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 02/04/2020 5 | * Copyright : S.Hamblett 6 | */ 7 | import 'package:convert/convert.dart'; 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:test/test.dart'; 10 | 11 | void main() { 12 | test('9-1', () { 13 | // 81 # array(1) 14 | // A3 # map(3) 15 | // 01 # unsigned(1) 16 | // A1 # map(1) 17 | // 01 # unsigned(1) 18 | // 01 # unsigned(1) 19 | // 03 # unsigned(3) 20 | // A1 # map(1) 21 | // 01 # unsigned(1) 22 | // 01 # unsigned(1) 23 | // 0C # unsigned(12) 24 | // A1 # map(1) 25 | // 01 # unsigned(1) 26 | // 01 # unsigned(1) 27 | 28 | const hexString = '81a301a1010103a101010ca10101'; 29 | final bytes = hex.decode(hexString); 30 | final decoded = cbor.decode(bytes); 31 | expect( 32 | decoded, 33 | CborList([ 34 | CborMap({ 35 | CborSmallInt(1): CborMap({CborSmallInt(1): CborSmallInt(1)}), 36 | CborSmallInt(3): CborMap({CborSmallInt(1): CborSmallInt(1)}), 37 | CborSmallInt(12): CborMap({CborSmallInt(1): CborSmallInt(1)}), 38 | }), 39 | ]), 40 | ); 41 | }); 42 | 43 | test('9-2', () { 44 | // 81 # array(1) 45 | // A3 # map(3) 46 | // 01 # unsigned(1) 47 | // A1 # map(1) 48 | // 01 # unsigned(1) 49 | // 02 # unsigned(2) 50 | // 03 # unsigned(3) 51 | // A1 # map(1) 52 | // 01 # unsigned(1) 53 | // 81 # array(1) 54 | // 01 # unsigned(1) 55 | // 0C # unsigned(12) 56 | // A1 # map(1) 57 | // 01 # unsigned(1) 58 | // 02 # unsigned(2) 59 | 60 | const hexString = '81A301A1010203A10181010CA10102'; 61 | final bytes = hex.decode(hexString); 62 | final decoded = cbor.decode(bytes); 63 | expect( 64 | decoded, 65 | CborList([ 66 | CborMap({ 67 | CborSmallInt(1): CborMap({CborSmallInt(1): CborSmallInt(2)}), 68 | CborSmallInt(3): CborMap({ 69 | CborSmallInt(1): CborList([CborSmallInt(1)]), 70 | }), 71 | CborSmallInt(12): CborMap({CborSmallInt(1): CborSmallInt(2)}), 72 | }), 73 | ]), 74 | ); 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 6.3.7 2 | - [Issue 75 - poc test only](https://github.com/shamblett/cbor/issues/75) 3 | - [PR 74](https://github.com/shamblett/cbor/pull/74) 4 | 5 | ### 6.3.6 6 | - [Issue 72](https://github.com/shamblett/cbor/issues/72) 7 | 8 | ### 6.3.5 9 | - [Issue 68](https://github.com/shamblett/cbor/issues/68) 10 | 11 | ### 6.3.4 12 | - [Issue 66](https://github.com/shamblett/cbor/issues/66) 13 | 14 | ### 6.3.3 15 | - [PR 65](https://github.com/shamblett/cbor/pull/65) 16 | 17 | ### 6.3.2 18 | - [PR 64](https://github.com/shamblett/cbor/pull/64) 19 | 20 | ### 6.3.1 21 | - [Issue 62](https://github.com/shamblett/cbor/issues/62) 22 | 23 | ### 6.3.0 24 | - [PR 60](https://github.com/shamblett/cbor/pull/60) 25 | 26 | ### 6.2.0 27 | - [PR 58](https://github.com/shamblett/cbor/pull/58) 28 | 29 | ### 6.1.1 30 | - [Issue 56](https://github.com/shamblett/cbor/issues/56) 31 | - [PR 57](https://github.com/shamblett/cbor/pull/57) 32 | 33 | ### 6.1.0 34 | - [Issue 51](https://github.com/shamblett/cbor/issues/51) 35 | - [PR 52](https://github.com/shamblett/cbor/pull/52) 36 | 37 | ### 6.0.0 38 | - [Issue 54](https://github.com/shamblett/cbor/issues/54) - Dart 3 39 | 40 | ### 5.1.2 41 | - [Issue 48](https://github.com/shamblett/cbor/issues/48) 42 | 43 | ### 5.1.1 44 | - [Issue 46](https://github.com/shamblett/cbor/issues/46) 45 | 46 | ### 5.1.0 47 | - [Issue 44](https://github.com/shamblett/cbor/issues/44) 48 | 49 | ### 5.0.1 50 | - [Issue 41](https://github.com/shamblett/cbor/issues/41) 51 | - [PR 42](https://github.com/shamblett/cbor/pull/42) 52 | 53 | ### 5.0.0 54 | 55 | ***Warning - major breaking API change in this version*** 56 | 57 | - [Issue 28](https://github.com/shamblett/cbor/issues/28) - API rewrite 58 | 59 | ### 4.1.0 60 | - [Issue 2](https://github.com/shamblett/cbor/issues/2) - Byte string keys added 61 | 62 | ### 4.0.2 63 | - [Issue 5](https://github.com/shamblett/cbor/issues/5) 64 | 65 | ### 4.0.1 66 | - [Issue 21](https://github.com/shamblett/cbor/issues/21) 67 | 68 | ### 4.0.0 69 | - [Issues 18 and 14 (NNBD)](https://github.com/shamblett/cbor/issues/18) 70 | - [Issues 14 (NNBD)](https://github.com/shamblett/cbor/issues/14) 71 | 72 | ### 3.2.0 73 | - [Issue 13](https://github.com/shamblett/cbor/issues/13) 74 | 75 | ### 3.1.0 76 | - [Issue 5](https://github.com/shamblett/cbor/issues/5) 77 | 78 | ### 3.0.0 79 | - [Issue 10](https://github.com/shamblett/cbor/issues/10) 80 | 81 | ### 2.1.0 82 | - [Issue 9](https://github.com/shamblett/cbor/issues/9) 83 | 84 | ### 2.0.5 85 | - Linter rules update 86 | 87 | ### 2.0.4 88 | - Linter rules update 89 | 90 | ### 2.0.3 91 | - [Issue 6](https://github.com/shamblett/cbor/issues/6) 92 | - Linter rules update 93 | 94 | ### 2.0.2 95 | - [Issue 4](https://github.com/shamblett/cbor/issues/4), update to Dart 2 96 | 97 | ### 1.0.4 98 | - Updates for pub. Remove json_mapper 99 | 100 | ### 1.0.3 101 | - Strong mode updates for pub. 102 | 103 | ### 1.0.2 104 | - SDK version constraint update 105 | 106 | ### 1.0.1 107 | - Minor update to the README, 108 | [issue 1](https://github.com/shamblett/cbor/issues/1). 109 | 110 | ## 1.0.0 111 | - Initial version 112 | -------------------------------------------------------------------------------- /test/rational_number_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : Alex Dochioiu 4 | * Date : 19/06/2024 5 | * Copyright : Alex Dochioiu 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:test/test.dart'; 10 | 11 | void main() { 12 | group( 13 | "RationalNumber", 14 | () => { 15 | group('encoding tests', () { 16 | test('encode auto length', () { 17 | expect( 18 | cbor.encode( 19 | CborRationalNumber( 20 | numerator: CborInt(BigInt.from(1)), 21 | denominator: CborSmallInt(2), 22 | type: CborLengthType.auto, 23 | ), 24 | ), 25 | [0xd8, 0x1e, 0x82, 0x1, 0x2], 26 | ); 27 | }); 28 | 29 | test('encode definite length', () { 30 | expect( 31 | cbor.encode( 32 | CborRationalNumber( 33 | numerator: CborInt(BigInt.from(1)), 34 | denominator: CborSmallInt(2), 35 | type: CborLengthType.definite, 36 | ), 37 | ), 38 | [0xd8, 0x1e, 0x82, 0x1, 0x2], 39 | ); 40 | }); 41 | 42 | test('encode indefinite length', () { 43 | expect( 44 | cbor.encode( 45 | CborRationalNumber( 46 | numerator: CborInt(BigInt.from(1)), 47 | denominator: CborSmallInt(2), 48 | type: CborLengthType.indefinite, 49 | ), 50 | ), 51 | [0xd8, 0x1e, 0x9f, 0x1, 0x2, 0xff], 52 | ); 53 | }); 54 | }), 55 | group('decoding tests', () { 56 | test('auto length', () { 57 | expect( 58 | cbor.decode([0xd8, 0x1e, 0x82, 0x1, 0x2]), 59 | CborRationalNumber( 60 | numerator: CborInt(BigInt.from(1)), 61 | denominator: CborSmallInt(2), 62 | type: CborLengthType.auto, 63 | ), 64 | ); 65 | }); 66 | 67 | test('definite length', () { 68 | expect( 69 | cbor.decode([0xd8, 0x1e, 0x82, 0x1, 0x2]), 70 | CborRationalNumber( 71 | numerator: CborInt(BigInt.from(1)), 72 | denominator: CborSmallInt(2), 73 | type: CborLengthType.definite, 74 | ), 75 | ); 76 | }); 77 | 78 | test('indefinite length', () { 79 | expect( 80 | cbor.decode([0xd8, 0x1e, 0x9f, 0x1, 0x2, 0xff]), 81 | CborRationalNumber( 82 | numerator: CborInt(BigInt.from(1)), 83 | denominator: CborSmallInt(2), 84 | type: CborLengthType.indefinite, 85 | ), 86 | ); 87 | }); 88 | 89 | test('not CborRationalNumber when denominator is 0', () { 90 | expect( 91 | cbor.decode([0xd8, 0x1e, 0x9f, 0x1, 0x0, 0xff]) 92 | is CborRationalNumber, 93 | false, 94 | ); 95 | 96 | expect( 97 | cbor.decode([0xd8, 0x1e, 0x9f, 0x1, 0x0, 0xff]) is CborList, 98 | true, 99 | ); 100 | }); 101 | }), 102 | }, 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /lib/src/value/internal.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | // This is for internal usage and should not be returned to the user 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:meta/meta.dart'; 12 | 13 | import '../encoder/sink.dart'; 14 | import '../utils/arg.dart'; 15 | 16 | class ToObjectOptions { 17 | final bool parseDateTime; 18 | final bool parseUri; 19 | final bool decodeBase64; 20 | final bool allowMalformedUtf8; 21 | 22 | ToObjectOptions({ 23 | required this.parseDateTime, 24 | required this.parseUri, 25 | required this.decodeBase64, 26 | required this.allowMalformedUtf8, 27 | }); 28 | } 29 | 30 | class ToJsonOptions { 31 | final JsonBytesEncoding encoding; 32 | final bool allowMalformedUtf8; 33 | final Object? substituteValue; 34 | 35 | ToJsonOptions({ 36 | required this.encoding, 37 | this.substituteValue, 38 | required this.allowMalformedUtf8, 39 | }); 40 | 41 | ToJsonOptions copyWith({JsonBytesEncoding? encoding}) => ToJsonOptions( 42 | encoding: encoding ?? this.encoding, 43 | substituteValue: substituteValue, 44 | allowMalformedUtf8: allowMalformedUtf8, 45 | ); 46 | } 47 | 48 | enum JsonBytesEncoding { base64Url, base64, base16 } 49 | 50 | @internal 51 | class Break with CborValueMixin implements CborValue { 52 | @override 53 | final List tags = const []; 54 | 55 | const Break(); 56 | 57 | @override 58 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 59 | throw UnimplementedError(); 60 | } 61 | 62 | @override 63 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 64 | throw UnimplementedError(); 65 | } 66 | 67 | @override 68 | void encode(EncodeSink sink) { 69 | sink.addHeaderInfo(CborMajorType.simpleFloat, Arg.indefiniteLength); 70 | } 71 | } 72 | 73 | @internal 74 | mixin CborValueMixin implements CborValue { 75 | @override 76 | Object? toObject({ 77 | bool parseDateTime = true, 78 | bool parseUri = true, 79 | bool decodeBase64 = false, 80 | bool allowMalformedUtf8 = false, 81 | }) => toObjectInternal( 82 | {}, 83 | ToObjectOptions( 84 | parseDateTime: parseDateTime, 85 | parseUri: parseUri, 86 | decodeBase64: decodeBase64, 87 | allowMalformedUtf8: allowMalformedUtf8, 88 | ), 89 | ); 90 | 91 | @override 92 | Object? toJson({Object? substituteValue, bool allowMalformedUtf8 = false}) => 93 | toJsonInternal( 94 | {}, 95 | ToJsonOptions( 96 | encoding: JsonBytesEncoding.base16, 97 | allowMalformedUtf8: allowMalformedUtf8, 98 | substituteValue: substituteValue, 99 | ), 100 | ); 101 | 102 | JsonBytesEncoding? get expectedConversion { 103 | var retVal = JsonBytesEncoding.base16; 104 | for (final tag in tags.reversed) { 105 | switch (tag) { 106 | case CborTag.expectedConversionToBase16: 107 | retVal = JsonBytesEncoding.base16; 108 | break; 109 | case CborTag.expectedConversionToBase64: 110 | retVal = JsonBytesEncoding.base64; 111 | break; 112 | case CborTag.expectedConversionToBase64Url: 113 | retVal = JsonBytesEncoding.base64Url; 114 | break; 115 | } 116 | } 117 | return retVal; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cbor 2 | [![Build Status](https://github.com/shamblett/cbor/actions/workflows/ci.yml/badge.svg)](https://github.com/shamblett/cbor/actions/workflows/ci.yml) 3 | [![Pub Version](https://shields.io/pub/v/cbor)](https://pub.dev/packages/cbor) 4 | 5 | [Documentation](https://pub.dev/documentation/cbor/latest/) 6 | 7 | This package provides an encoding/decoding package for the Concise Binary Object 8 | Representation as documented in [RFC8949](https://www.rfc-editor.org/rfc/rfc8949.html). 9 | 10 | CBOR is increasingly used as a line protocol in the IOT world where the overhead 11 | of transmitting JSON on constrained devices can be large in packet size and 12 | processing overheads. 13 | 14 | ## Features 15 | 16 | * Streamlined encoding and decoding of CBOR, including indefinite length and 17 | streamed decoding. 18 | * Set of types that semantically handles types, nesting, and tags. 19 | * Automatic conversion for the smallest size needed for integers. 20 | * Automatic conversion for the smallest precision needed for floating point 21 | numbers. 22 | * Any value can be the key for a map. 23 | * Optional simple API for encoding and decoding directly to Dart objects. 24 | * Pretty-printing and conversion to JSON. 25 | * Rational numbering encoding is supported on tag 30. 26 | 27 | ## Usage 28 | 29 | Two APIs are provided, `cbor.dart` and `simple.dart`. 30 | 31 | ### `cbor.dart` 32 | 33 | Encodes from `CborValue` and decodes to `CborValue`. 34 | 35 | The `CborValue` contains the tags for this item, and its subtypes are used to 36 | identify the type and tags for the item. This allows one to handle information 37 | encoded in a more lossless way. 38 | 39 | Inheritance tree for `CborValue` looks something like this: 40 | 41 | ``` 42 | CborValue 43 | ├── CborInt 44 | │ ├── CborSmallInt 45 | │ └── CborDateTimeInt 46 | ├── CborBytes 47 | │ └── CborBigInt 48 | ├── CborString 49 | │ ├── CborDateTimeString 50 | │ ├── CborBase64 51 | │ ├── CborBase64Url 52 | │ ├── CborMime 53 | │ └── CborRegex 54 | ├── CborFloat 55 | │ └── CborDateTimeFloat 56 | ├── CborSimpleValue 57 | │ ├── CborNull 58 | │ ├── CborBool 59 | │ └── CborUndefined 60 | ├── CborMap 61 | └── CborList 62 | ├── CborDecimalFraction 63 | ├── CborRationalNumber 64 | └── CborBigFloat 65 | ``` 66 | 67 | #### Example 68 | 69 | ```dart 70 | import 'package:cbor/cbor.dart'; 71 | 72 | test('{1:2,3:4}', () { 73 | final encoded = cbor.encode(CborMap({ 74 | CborSmallInt(1): CborSmallInt(2), 75 | CborSmallInt(3): CborSmallInt(4), 76 | })); 77 | expect(encoded, [0xa2, 0x01, 0x02, 0x03, 0x04]); 78 | }); 79 | ``` 80 | 81 | [Check out the example folder for more examples](https://github.com/shamblett/cbor/tree/master/example). 82 | 83 | ### `simple.dart` 84 | 85 | Less powerful, but may be the best option for simple applications that do not 86 | need any of the fancy features of the `cbor` API above. 87 | 88 | The encoder in simple API will operate similarly to constructing a `CborValue` 89 | from the input and the encoding it. 90 | 91 | Decoder will translate the input to a common Dart object, ignoring any extra 92 | tags after the type is decided. 93 | 94 | #### Example 95 | 96 | ```dart 97 | import 'package:cbor/simple.dart'; 98 | 99 | test('{1:2,3:4}', () { 100 | final encoded = cbor.encode({ 101 | 1: 2, 102 | 3: 4, 103 | }); 104 | expect(encoded, [0xa2, 0x01, 0x02, 0x03, 0x04]); 105 | }); 106 | ``` 107 | -------------------------------------------------------------------------------- /lib/src/value/simple_value.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:collection/collection.dart'; 10 | 11 | import '../encoder/sink.dart'; 12 | import '../utils/arg.dart'; 13 | import 'internal.dart'; 14 | 15 | /// A CBOR simple value without any 16 | /// additional content. 17 | 18 | abstract class CborSimpleValue extends CborValue { 19 | int get simpleValue; 20 | 21 | const factory CborSimpleValue(int simpleValue, {List tags}) = 22 | _CborSimpleValueImpl; 23 | } 24 | 25 | class _CborSimpleValueImpl with CborValueMixin implements CborSimpleValue { 26 | @override 27 | final int simpleValue; 28 | 29 | @override 30 | final List tags; 31 | 32 | @override 33 | int get hashCode => Object.hash(simpleValue, Object.hashAll(tags)); 34 | 35 | const _CborSimpleValueImpl(this.simpleValue, {this.tags = const []}); 36 | 37 | @override 38 | String toString() => simpleValue.toString(); 39 | @override 40 | bool operator ==(Object other) => 41 | other is CborSimpleValue && 42 | tags.equals(other.tags) && 43 | other.simpleValue == simpleValue; 44 | 45 | @override 46 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 47 | return simpleValue; 48 | } 49 | 50 | @override 51 | void encode(EncodeSink sink) { 52 | sink.addTags(tags); 53 | 54 | sink.addHeaderInfo(CborMajorType.simpleFloat, Arg.int(simpleValue)); 55 | } 56 | 57 | @override 58 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 59 | return o.substituteValue; 60 | } 61 | } 62 | 63 | /// A CBOR null value. 64 | abstract class CborNull extends CborSimpleValue { 65 | const factory CborNull({List tags}) = _CborNullImpl; 66 | } 67 | 68 | class _CborNullImpl extends _CborSimpleValueImpl implements CborNull { 69 | const _CborNullImpl({List tags = const []}) 70 | : super(CborAdditionalInfo.simpleNull, tags: tags); 71 | 72 | @override 73 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 74 | return null; 75 | } 76 | 77 | @override 78 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 79 | return null; 80 | } 81 | } 82 | 83 | /// A CBOR undefined value. 84 | abstract class CborUndefined extends CborSimpleValue { 85 | const factory CborUndefined({List tags}) = _CborUndefinedImpl; 86 | } 87 | 88 | class _CborUndefinedImpl extends _CborSimpleValueImpl implements CborUndefined { 89 | const _CborUndefinedImpl({List tags = const []}) 90 | : super(CborAdditionalInfo.simpleUndefined, tags: tags); 91 | 92 | @override 93 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 94 | return null; 95 | } 96 | 97 | @override 98 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 99 | return o.substituteValue; 100 | } 101 | } 102 | 103 | /// A CBOR boolean value. 104 | abstract class CborBool extends CborSimpleValue { 105 | bool get value; 106 | 107 | const factory CborBool(bool value, {List tags}) = _CborBoolImpl; 108 | } 109 | 110 | class _CborBoolImpl extends _CborSimpleValueImpl implements CborBool { 111 | @override 112 | final bool value; 113 | 114 | const _CborBoolImpl(this.value, {List tags = const []}) 115 | : super( 116 | !value ? CborAdditionalInfo.simpleFalse : CborAdditionalInfo.simpleTrue, 117 | tags: tags, 118 | ); 119 | 120 | @override 121 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 122 | return value; 123 | } 124 | 125 | @override 126 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 127 | return value; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/src/encoder/sink.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:typed_data'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:typed_data/typed_buffers.dart'; 12 | import 'package:characters/characters.dart'; 13 | 14 | import '../constants.dart'; 15 | import '../utils/arg.dart'; 16 | 17 | abstract class EncodeSink implements Sink> { 18 | final Set _references = {}; 19 | 20 | EncodeSink(); 21 | 22 | factory EncodeSink.withBuffer(Uint8Buffer b) => _BufferEncodeSink(b); 23 | 24 | factory EncodeSink.withSink(Sink> x) => _EncodeSink(x); 25 | 26 | void addToCycleCheck(Object object) { 27 | if (!_references.add(object)) { 28 | throw CborCyclicError(object); 29 | } 30 | } 31 | 32 | void removeFromCycleCheck(Object it) { 33 | _references.remove(it); 34 | } 35 | 36 | @override 37 | void close() { 38 | return; 39 | } 40 | 41 | void addTags(List tags) { 42 | for (final value in tags) { 43 | addHeaderInfo(CborMajorType.tag, Arg.int(value)); 44 | } 45 | } 46 | 47 | void addHeader(int majorType, int additionalInfo) { 48 | add([(majorType << CborMajorType.map) | additionalInfo]); 49 | } 50 | 51 | void addHeaderInfo(int majorType, Arg info) { 52 | if (info.isIndefiniteLength) { 53 | addHeader(majorType, CborConstants.additionalInfoBitMask); 54 | } else if (info.isValidInt) { 55 | final int = info.toInt(); 56 | 57 | if (int <= CborAdditionalInfo.simpleValueLow) { 58 | addHeader(majorType, info.toInt()); 59 | return; 60 | } else if (int.bitLength <= CborConstants.byteLength) { 61 | addHeader(majorType, CborAdditionalInfo.simpleValueHigh); 62 | add([int]); 63 | } else if (int.bitLength <= CborConstants.sixteen) { 64 | addHeader(majorType, CborAdditionalInfo.halfPrecisionFloat); 65 | final x = Uint8List(CborConstants.two); 66 | ByteData.view(x.buffer).setUint16(0, info.toInt()); 67 | add(x); 68 | } else if (int.bitLength <= CborConstants.bitsPerWord) { 69 | addHeader(majorType, CborAdditionalInfo.singlePrecisionFloat); 70 | final x = Uint8List(CborConstants.bytesPerWord); 71 | ByteData.view(x.buffer).setUint32(0, info.toInt()); 72 | add(x); 73 | } else { 74 | addHeader(majorType, CborAdditionalInfo.doublePrecisionFloat); 75 | add(u64BytesHelper(info.toInt())); 76 | } 77 | } else { 78 | addHeader(majorType, CborAdditionalInfo.doublePrecisionFloat); 79 | final x = Uint8List(CborConstants.byteLength); 80 | final infoBigInt = info.toBigInt(); 81 | ByteData.view(x.buffer).setUint32( 82 | 0, 83 | (infoBigInt >> CborConstants.bitsPerWord) 84 | .toUnsigned(CborConstants.bitsPerWord) 85 | .toInt(), 86 | ); 87 | ByteData.view(x.buffer).setUint32( 88 | CborConstants.bytesPerWord, 89 | infoBigInt.toUnsigned(CborConstants.bitsPerWord).toInt(), 90 | ); 91 | add(x); 92 | } 93 | } 94 | } 95 | 96 | class _BufferEncodeSink extends EncodeSink { 97 | final Uint8Buffer buffer; 98 | 99 | _BufferEncodeSink(this.buffer); 100 | 101 | @override 102 | void add(List data) { 103 | buffer.addAll(data); 104 | } 105 | } 106 | 107 | class _EncodeSink extends EncodeSink { 108 | final Sink> _sink; 109 | 110 | _EncodeSink(this._sink); 111 | 112 | @override 113 | void close() { 114 | _sink.close(); 115 | } 116 | 117 | @override 118 | void add(List data) { 119 | _sink.add(data); 120 | } 121 | } 122 | 123 | Uint8List u64BytesHelper(int x) { 124 | String rstr = x.toRadixString(CborConstants.binRadix); 125 | while (rstr.length < CborConstants.bitsPerDoubleWord) { 126 | rstr = '0$rstr'; 127 | } 128 | List bytes = []; 129 | for (int i = 0; i < CborConstants.byteLength; i++) { 130 | bytes.add( 131 | int.parse( 132 | '${rstr.characters.getRange(i * CborConstants.byteLength, i * CborConstants.byteLength + CborConstants.byteLength)}', 133 | radix: CborConstants.binRadix, 134 | ), 135 | ); 136 | } 137 | return Uint8List.fromList(bytes); 138 | } 139 | -------------------------------------------------------------------------------- /lib/src/utils/utils.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | 10 | import '../constants.dart'; 11 | 12 | extension IterableExt on Iterable { 13 | Iterable> chunks(int length) sync* { 14 | final iterator = this.iterator; 15 | 16 | while (true) { 17 | final chunk = []; 18 | for (var i = 0; i < length; i++) { 19 | if (!iterator.moveNext()) { 20 | return; 21 | } 22 | 23 | chunk.add(iterator.current); 24 | } 25 | 26 | yield chunk; 27 | } 28 | } 29 | } 30 | 31 | extension SinkExt on Sink { 32 | Sink map(T Function(U) conversion) => _MapSink(conversion, this); 33 | } 34 | 35 | class _MapSink implements Sink { 36 | final T Function(U) map; 37 | final Sink sink; 38 | 39 | _MapSink(this.map, this.sink); 40 | 41 | @override 42 | void add(U data) { 43 | sink.add(map(data)); 44 | } 45 | 46 | @override 47 | void close() { 48 | sink.close(); 49 | } 50 | } 51 | 52 | extension DateTimeExtension on DateTime { 53 | /// Will ommit milliseconds if it is 0. 54 | /// 55 | /// Will trim second fraction. 56 | /// 57 | /// Will add time zone. 58 | String toInternetIso8601String(Duration? timeZoneOffset) { 59 | var x = this; 60 | if (timeZoneOffset == null) { 61 | timeZoneOffset = x.timeZoneOffset; 62 | x = x.toUtc(); 63 | } 64 | 65 | final String y; 66 | if (x.year.abs() < CborConstants.maxYear) { 67 | final ySign = x.year < 0 ? '-' : ''; 68 | y = ySign + x.year.abs().toString().padLeft(CborConstants.four, '0'); 69 | } else { 70 | final ySign = x.year < 0 ? '-' : '+'; 71 | y = ySign + x.year.abs().toString().padLeft(CborConstants.six, '0'); 72 | } 73 | 74 | final m = x.month.toString().padLeft(CborConstants.two, '0'); 75 | final d = x.day.toString().padLeft(CborConstants.two, '0'); 76 | final h = x.hour.toString().padLeft(CborConstants.two, '0'); 77 | final min = x.minute.toString().padLeft(CborConstants.two, '0'); 78 | final sec = x.second.toString().padLeft(CborConstants.two, '0'); 79 | 80 | final String secFraction; 81 | if (x.millisecond == 0) { 82 | secFraction = ''; 83 | } else { 84 | final ms = x.millisecond.toString().padLeft(CborConstants.three, '0'); 85 | final us = 86 | x.microsecond != 0 87 | ? x.microsecond.toString().padLeft(CborConstants.three, '0') 88 | : ''; 89 | secFraction = '.$ms$us'.replaceAll(RegExp('0*\$'), ''); 90 | } 91 | 92 | final String timeZone; 93 | if (timeZoneOffset.inMinutes == 0) { 94 | timeZone = 'Z'; 95 | } else { 96 | final timeZoneTotalMin = timeZoneOffset.inMinutes.abs(); 97 | final timeZoneSign = !timeZoneOffset.isNegative ? '+' : '-'; 98 | final timeZoneHour = (timeZoneTotalMin ~/ CborConstants.seconds) 99 | .toString() 100 | .padLeft(CborConstants.two, '0'); 101 | final timeZoneMin = (timeZoneTotalMin % CborConstants.seconds) 102 | .toString() 103 | .padLeft(CborConstants.two, '0'); 104 | 105 | timeZone = '$timeZoneSign$timeZoneHour:$timeZoneMin'; 106 | } 107 | 108 | return '$y-$m-${d}T$h:$min:$sec$secFraction$timeZone'; 109 | } 110 | } 111 | 112 | /// Returns whether T is a subtype of U. 113 | bool isSubtype() => _Helper() is _Helper; 114 | 115 | class _Helper { 116 | const _Helper(); 117 | } 118 | 119 | bool isHintSubtype(int hint) { 120 | switch (hint) { 121 | case CborTag.dateTimeString: 122 | case CborTag.epochDateTime: 123 | case CborTag.positiveBignum: 124 | case CborTag.negativeBignum: 125 | case CborTag.bigFloat: 126 | case CborTag.encodedCborData: 127 | case CborTag.uri: 128 | case CborTag.base64Url: 129 | case CborTag.base64: 130 | case CborTag.regex: 131 | case CborTag.mime: 132 | return true; 133 | } 134 | 135 | return false; 136 | } 137 | 138 | bool isExpectConversion(int tag) { 139 | switch (tag) { 140 | case CborTag.expectedConversionToBase16: 141 | case CborTag.expectedConversionToBase64: 142 | case CborTag.expectedConversionToBase64Url: 143 | return true; 144 | } 145 | 146 | return false; 147 | } 148 | -------------------------------------------------------------------------------- /test/json_encoder_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:typed_data'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:test/test.dart'; 12 | 13 | String encode(Object? input) { 14 | return const CborJsonEncoder().convert(CborValue(input)); 15 | } 16 | 17 | void main() { 18 | group('RFC Appendix A Diagnostics encoder tests -> ', () { 19 | test('0', () { 20 | expect(encode(0), '0'); 21 | }); 22 | 23 | test('100', () { 24 | expect(encode(100), '100'); 25 | }); 26 | 27 | test('18446744073709551615', () { 28 | expect( 29 | encode(BigInt.parse('18446744073709551615')), 30 | '"FFFFFFFFFFFFFFFF"', 31 | ); 32 | }); 33 | 34 | test('18446744073709551616', () { 35 | expect( 36 | encode(BigInt.parse('18446744073709551616')), 37 | '"010000000000000000"', 38 | ); 39 | }); 40 | 41 | test('-18446744073709551616', () { 42 | expect( 43 | encode(BigInt.parse('-18446744073709551616')), 44 | '"~FFFFFFFFFFFFFFFF"', 45 | ); 46 | }); 47 | 48 | test('-18446744073709551617', () { 49 | expect( 50 | encode(BigInt.parse('-18446744073709551617')), 51 | '"~010000000000000000"', 52 | ); 53 | }); 54 | 55 | test('-1', () { 56 | expect(encode(-1), '-1'); 57 | }); 58 | 59 | test('0.0', () { 60 | expect(encode(0.0), '0.0'); 61 | }); 62 | 63 | test('-0.0', () { 64 | expect(encode(-0.0), '-0.0'); 65 | }); 66 | 67 | test('1.0', () { 68 | expect(encode(1.0), '1.0'); 69 | }); 70 | 71 | test('1.5', () { 72 | expect(encode(1.5), '1.5'); 73 | }); 74 | 75 | test('65504.0', () { 76 | expect(encode(65504.0), '65504.0'); 77 | }); 78 | 79 | test('100000.0', () { 80 | expect(encode(100000.0), '100000.0'); 81 | }); 82 | 83 | test('3.4028234663852886e+38', () { 84 | expect(encode(3.4028234663852886e+38), '3.4028234663852886e+38'); 85 | }); 86 | 87 | test('1.0e+300', () { 88 | expect(encode(1.0e+300), '1e+300'); 89 | }); 90 | 91 | test('5.960464477539063e-8', () { 92 | expect(encode(5.960464477539063e-8), '5.960464477539063e-8'); 93 | }); 94 | 95 | test('0.00006103515625', () { 96 | expect(encode(0.00006103515625), '0.00006103515625'); 97 | }); 98 | 99 | test('Infinity', () { 100 | expect(encode(double.infinity), 'null'); 101 | }); 102 | 103 | test('Nan', () { 104 | expect(encode(double.nan), 'null'); 105 | }); 106 | 107 | test('-Infinity', () { 108 | expect(encode(double.negativeInfinity), 'null'); 109 | }); 110 | 111 | test('False', () { 112 | expect(encode(false), 'false'); 113 | }); 114 | 115 | test('True', () { 116 | expect(encode(true), 'true'); 117 | }); 118 | 119 | test('Null', () { 120 | expect(encode(null), 'null'); 121 | }); 122 | 123 | test('Empty single quotes', () { 124 | expect(encode(Uint8List.fromList([])), '""'); 125 | }); 126 | 127 | test('4 bytes', () { 128 | expect(encode(Uint8List.fromList([0x1, 0x2, 0x3, 0x4])), '"01020304"'); 129 | }); 130 | 131 | test('Quoted backslash', () { 132 | expect(encode('\\'), '"\\\\"'); 133 | }); 134 | 135 | test('New line', () { 136 | expect(encode('\n'), '"\\n"'); 137 | }); 138 | 139 | test('Unicode ü', () { 140 | expect(encode('ü'), '"ü"'); 141 | }); 142 | 143 | test('Unicode 水', () { 144 | expect(encode('水'), '"水"'); 145 | }); 146 | 147 | test('Array empty', () { 148 | expect(encode([]), '[]'); 149 | }); 150 | 151 | test('Array 1,2,3', () { 152 | expect(encode([1, 2, 3]), '[1,2,3]'); 153 | }); 154 | 155 | test('Array 1,[2,3],[4,5]', () { 156 | expect( 157 | encode([ 158 | 1, 159 | [2, 3], 160 | [4, 5], 161 | ]), 162 | '[1,[2,3],[4,5]]', 163 | ); 164 | }); 165 | 166 | test('{}', () { 167 | expect(encode({}), '{}'); 168 | }); 169 | 170 | test('{a:1,b:[2,3]}', () { 171 | expect( 172 | encode({ 173 | 'a': 1, 174 | 'b': [2, 3], 175 | }), 176 | '{"a":1,"b":[2,3]}', 177 | ); 178 | }); 179 | 180 | test('[a, {b:c}]', () { 181 | expect( 182 | encode([ 183 | 'a', 184 | {'b': 'c'}, 185 | ]), 186 | '["a",{"b":"c"}]', 187 | ); 188 | }); 189 | }); 190 | } 191 | -------------------------------------------------------------------------------- /test/issues/issue51_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : A. Dochioiu 4 | * Date : 11/04/2023 5 | * Copyright : A.Dochioiu 6 | */ 7 | import 'package:cbor/cbor.dart'; 8 | import 'package:hex/hex.dart'; 9 | import 'package:test/test.dart'; 10 | 11 | void main() { 12 | test( 13 | 'lists do not change definite/indefinite type when parsing and re-encoding', 14 | () { 15 | final codec = CborCodec().fuse(HEX); 16 | final expected = 17 | "84a6008482582069f647781a51876c03bce5915cafc494f45456dec10d2cf7d6a730dcd338ce7101825820b187362541bcede39962010795394b2d6a86b943b2bd8db3ed64044e199b316701825820b187362541bcede39962010795394b2d6a86b943b2bd8db3ed64044e199b316702825820fd67551fa18fc075a8687cb2621378ccbdf55d66b10c492753d38e29e491673903018383583911a65ca58a4e9c755fa830173d2a5caed458ac0c73f97db7faae2e7e3b52563c5410bff6a0d43ccebb7c37e1f69f5eb260552521adff33b9c21a0cef1a665820a73e566454372ae65eb101d0f14a6e507fdb9593160269b476f595f7658dc34982583901ed49d9adbd06592290b9a16032375d6b79d4df760cad0d9bca9555fc4199f66b16ce9eb5849ed96473face025b2e9bcbdf1e352ad43629811abff58df482583901ed49d9adbd06592290b9a16032375d6b79d4df760cad0d9bca9555fc4199f66b16ce9eb5849ed96473face025b2e9bcbdf1e352ad4362981821a003630d0ad581c0e14267a8020229adc0184dd25fa3174c3f7d6caadcb4425c70e7c04a14a756e736967303131313801581c160a880d9fc45380737cb7e57ff859763230aab28b3ef6a84007bfcca1444d4952411864581c16fdd33c86af604e837ae57d79d5f0f1156406086db5f16afb3fcf51a14544474f4c441a05f5e100581c1f362a4df39f451401e44fee30f27eb39712d66aae375f539be94ed6a14c546865496c6961643238303401581c29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6a1434d494e1a00701aae581c2eeb39204c3d104b40ec56bfd612bb1067fdea45de15f3a8aad9db71a14d4d616769634b6f6e673235303201581c682fe60c9918842b3323c43b5144bc3d52a23bd2fb81345560d73f63a1444e45574d1a004c4b40581ca0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235a145484f534b591a000f4240581caf2e27f580f7f08e93190a81f72462f153026d06450924726645891ba144445249501a7de7b663581cb6a7467ea1deb012808ef4e87b5ff371e85f7142d7b356a40d9b42a0a1581e436f726e75636f70696173205b76696120436861696e506f72742e696f5d1a004c4b40581cc68307e7ca850513507f1498862a57c7f4fae7ba8e84b8bc074093a9a14444494253191388581cdfc825363afcdff0f5ec3171f0a29b1d345d271714aa9b2a552b6bbfa148416e74546f6b656e1832581ce4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86a158206aa2153e1ae896a95539c9d62f76cedcdabdcdf144e564b8955f609d660cf6a21a0211e121021a000355c5031a0557e4ee075820b64602eebf602e8bbce198e2a1d6bbb2a109ae87fa5316135d217110d6d946490b58205109fa2ce7968f8cc211dba68e338a4b620db1a35108aad7ff6b953ad86890dba1049fd8799fd8799fd8799f581ced49d9adbd06592290b9a16032375d6b79d4df760cad0d9bca9555fcffd8799fd8799fd8799f581c4199f66b16ce9eb5849ed96473face025b2e9bcbdf1e352ad4362981ffffffffd8799fd8799f581ced49d9adbd06592290b9a16032375d6b79d4df760cad0d9bca9555fcffd8799fd8799fd8799f581c4199f66b16ce9eb5849ed96473face025b2e9bcbdf1e352ad4362981ffffffffd87a80d8799fd8799f581c29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6434d494eff1a84369796ff1a001e76a61a001e8480fffff5a11902a2a1636d736781781c4d696e737761703a205377617020457861637420496e204f72646572"; 18 | final cbor = codec.decode(expected); 19 | final actual = codec.encode(cbor); 20 | 21 | expect(actual, expected); 22 | }, 23 | ); 24 | 25 | test( 26 | 'indefinite length maps do not change definite/indefinite type when parsing and re-encoding', 27 | () { 28 | // This is an indefinite length map 29 | final expected = [ 30 | 0xbf, 31 | 0x63, 32 | 0x64, 33 | 0x75, 34 | 0x6e, 35 | 0xf5, 36 | 0x63, 37 | 0x41, 38 | 0x6d, 39 | 0x74, 40 | 0x21, 41 | 0xff, 42 | ]; 43 | final cbor = cborDecode(expected); 44 | final actual = cborEncode(cbor); 45 | 46 | expect(actual, expected); 47 | }, 48 | ); 49 | 50 | test( 51 | 'definite length maps do not change definite/indefinite type when parsing and re-encoding', 52 | () { 53 | // This is an definite length map 54 | final expected = [ 55 | 0xa2, 56 | 0x63, 57 | 0x64, 58 | 0x75, 59 | 0x6e, 60 | 0xf5, 61 | 0x63, 62 | 0x41, 63 | 0x6d, 64 | 0x74, 65 | 0x21, 66 | ]; 67 | final cbor = cborDecode(expected); 68 | final actual = cborEncode(cbor); 69 | 70 | expect(actual, expected); 71 | }, 72 | ); 73 | 74 | test( 75 | 'definite length byte arrays do not change definite/indefinite type when parsing and re-encoding', 76 | () { 77 | // This is an indefinite length byte array 78 | final expected = [0x5f, 0x42, 0x01, 0x02, 0x43, 0x03, 0x04, 0x05, 0xff]; 79 | final cbor = cborDecode(expected); 80 | final actual = cborEncode(cbor); 81 | 82 | expect(actual, expected); 83 | }, 84 | ); 85 | test( 86 | 'indefinite length byte arrays do not change definite/indefinite type when parsing and re-encoding', 87 | () { 88 | // This is an definite length byte array 89 | final expected = [0x45, 0x01, 0x02, 0x03, 0x04, 0x05]; 90 | final cbor = cborDecode(expected); 91 | final actual = cborEncode(cbor); 92 | 93 | expect(actual, expected); 94 | }, 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /lib/src/decoder/pretty_print.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:ieee754/ieee754.dart'; 11 | 12 | import 'stage1.dart'; 13 | import '../constants.dart'; 14 | import '../value/value.dart'; 15 | 16 | /// Pretty print a CBOR input. 17 | /// 18 | /// Example output with `indent = 2` for `{1: "one", 2: "two"}`: 19 | /// 20 | /// ```txt 21 | /// a2 (map length 2) 22 | /// 1 (int 1) 23 | /// 63 6f 6e 65 (string "one") 24 | /// 2 (int 2) 25 | /// 63 74 77 6f (string "two") 26 | /// ``` 27 | String cborPrettyPrint( 28 | List input, { 29 | int indent = CborConstants.prettyPrintIndent, 30 | }) { 31 | final prettyPrint = _PrettyPrint(input, indent: indent); 32 | RawSink(prettyPrint) 33 | ..add(input) 34 | ..close(); 35 | 36 | return prettyPrint.writer.toString(); 37 | } 38 | 39 | class _PrettyPrint implements Sink { 40 | final StringBuffer writer = StringBuffer(); 41 | final List data; 42 | final List<_Nesting> nested = []; 43 | final int indent; 44 | 45 | _PrettyPrint(this.data, {required this.indent}); 46 | 47 | @override 48 | void close() { 49 | return; 50 | } 51 | 52 | @override 53 | void add(RawValue x) { 54 | final indentation = ' ' * indent * nested.length; 55 | 56 | writer.write(indentation); 57 | writer.writeAll( 58 | data 59 | .getRange(x.start, x.end) 60 | .map((by) => '${by.toRadixString(CborConstants.hexRadix)} '), 61 | ); 62 | 63 | if (nested.isNotEmpty) { 64 | var remainingItems = nested.last.remainingItems; 65 | if (remainingItems != null) { 66 | remainingItems -= 1; 67 | nested.last.remainingItems = remainingItems; 68 | 69 | if (remainingItems <= 0) { 70 | nested.removeLast(); 71 | } 72 | } 73 | } 74 | 75 | switch (x.header.majorType) { 76 | case CborMajorType.uint: 77 | writer.write('(int ${x.header.arg.toBigInt()})'); 78 | break; 79 | case CborMajorType.nint: 80 | writer.write('(int ${~x.header.arg.toBigInt()})'); 81 | break; 82 | case CborMajorType.byteString: 83 | final length = x.header.arg; 84 | if (length.isIndefiniteLength) { 85 | writer.write('(indefinite length bytes)'); 86 | nested.add(_Nesting(null)); 87 | } else { 88 | writer.write('(bytes)'); 89 | } 90 | break; 91 | case CborMajorType.textString: 92 | final length = x.header.arg; 93 | if (length.isIndefiniteLength) { 94 | writer.write('(indefinite length string)'); 95 | nested.add(_Nesting(null)); 96 | } else { 97 | writer.write( 98 | '(string "${(const Utf8Codec(allowMalformed: true)).decode(x.data)}")', 99 | ); 100 | } 101 | break; 102 | case CborMajorType.array: 103 | final length = x.header.arg; 104 | if (length.isIndefiniteLength) { 105 | writer.write('(indefinite length array)'); 106 | nested.add(_Nesting(null)); 107 | } else { 108 | writer.write('(array length ${length.toInt()})'); 109 | nested.add(_Nesting(length.toInt())); 110 | } 111 | break; 112 | case CborMajorType.map: 113 | final length = x.header.arg; 114 | if (length.isIndefiniteLength) { 115 | writer.write('(indefinite length map)'); 116 | nested.add(_Nesting(null)); 117 | } else { 118 | writer.write('(map length ${length.toInt()})'); 119 | nested.add( 120 | _Nesting(length.toInt() * CborConstants.prettyPrintIndent), 121 | ); 122 | } 123 | break; 124 | case CborMajorType.tag: 125 | writer.write('(tag ${x.header.arg.toInt()})'); 126 | break; 127 | case CborMajorType.simpleFloat: 128 | switch (x.header.additionalInfo) { 129 | case CborAdditionalInfo.simpleFalse: 130 | writer.write('(false)'); 131 | break; 132 | case CborAdditionalInfo.simpleTrue: 133 | writer.write('(true)'); 134 | break; 135 | case CborAdditionalInfo.simpleNull: 136 | writer.write('(null)'); 137 | break; 138 | case CborAdditionalInfo.simpleUndefined: 139 | writer.write('(undefined)'); 140 | break; 141 | case CborAdditionalInfo.halfPrecisionFloat: 142 | writer.write( 143 | '(${FloatParts.fromFloat16Bytes(x.header.dataBytes).toDouble()})', 144 | ); 145 | break; 146 | case CborAdditionalInfo.singlePrecisionFloat: 147 | writer.write( 148 | '(${FloatParts.fromFloat32Bytes(x.header.dataBytes).toDouble()})', 149 | ); 150 | break; 151 | case CborAdditionalInfo.doublePrecisionFloat: 152 | writer.write( 153 | '(${FloatParts.fromFloat64Bytes(x.header.dataBytes).toDouble()})', 154 | ); 155 | break; 156 | case CborAdditionalInfo.breakStop: 157 | writer.write('(break)'); 158 | nested.removeLast(); 159 | break; 160 | 161 | default: 162 | writer.write('(simple ${x.header.arg.toInt()})'); 163 | break; 164 | } 165 | } 166 | 167 | writer.write('\n'); 168 | } 169 | } 170 | 171 | class _Nesting { 172 | int? remainingItems; 173 | 174 | _Nesting(this.remainingItems); 175 | } 176 | -------------------------------------------------------------------------------- /lib/src/value/int.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | 10 | import '../encoder/sink.dart'; 11 | import 'package:collection/collection.dart'; 12 | import '../constants.dart'; 13 | import '../utils/arg.dart'; 14 | import 'internal.dart'; 15 | 16 | /// A CBOR integer or big number. 17 | /// 18 | /// Because CBOR integers can be up to 64-bit, which JS cannot represent, 19 | /// they are converted to [BigInt]. 20 | /// 21 | /// If the incoming value has less than 53 bits, it is [CborSmallInt]. 22 | abstract class CborInt extends CborValue { 23 | factory CborInt(BigInt value, {List? tags}) { 24 | if (value.isValidInt) { 25 | return CborSmallInt(value.toInt(), tags: tags ?? const []); 26 | } 27 | 28 | final bitLength = value.isNegative ? (~value).bitLength : value.bitLength; 29 | 30 | return bitLength <= CborConstants.bitsPerDoubleWord 31 | ? _LargeInt(value, tags ?? const []) 32 | : CborBigInt(value, tags); 33 | } 34 | 35 | /// Return the value as a [BigInt]. 36 | BigInt toBigInt(); 37 | 38 | /// Return the value as [int]. 39 | /// 40 | /// If the number does not fit, clamps to the max (or min) integer. 41 | int toInt(); 42 | } 43 | 44 | /// A CBOR integer which can be represented losslessly as [int]. 45 | abstract class CborSmallInt extends CborInt { 46 | int get value; 47 | 48 | const factory CborSmallInt(int value, {List tags}) = _CborSmallIntImpl; 49 | } 50 | 51 | class _CborSmallIntImpl with CborValueMixin implements CborSmallInt { 52 | @override 53 | final int value; 54 | 55 | @override 56 | final List tags; 57 | 58 | @override 59 | int get hashCode => Object.hash(value, Object.hashAll(tags)); 60 | 61 | const _CborSmallIntImpl(this.value, {this.tags = const []}); 62 | 63 | @override 64 | String toString() => value.toString(); 65 | @override 66 | bool operator ==(Object other) => 67 | other is CborSmallInt && 68 | tags.equals(other.tags) && 69 | value == other.toInt(); 70 | 71 | @override 72 | BigInt toBigInt() => BigInt.from(value); 73 | @override 74 | int toInt() => value; 75 | 76 | @override 77 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 78 | return value; 79 | } 80 | 81 | @override 82 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 83 | return value.bitLength < CborConstants.jsonBitLength 84 | ? value 85 | : CborBigInt(BigInt.from(value)).toJsonInternal(cyclicCheck, o); 86 | } 87 | 88 | @override 89 | void encode(EncodeSink sink) { 90 | sink.addTags(tags); 91 | 92 | if (!value.isNegative) { 93 | sink.addHeaderInfo(0, Arg.int(value)); 94 | } else { 95 | sink.addHeaderInfo(1, Arg.int((~BigInt.from(value)).toInt())); 96 | } 97 | } 98 | } 99 | 100 | class _LargeInt with CborValueMixin implements CborInt { 101 | final BigInt value; 102 | 103 | @override 104 | final List tags; 105 | 106 | @override 107 | int get hashCode => Object.hash(value, Object.hashAll(tags)); 108 | 109 | _LargeInt(this.value, this.tags); 110 | 111 | @override 112 | String toString() => value.toString(); 113 | @override 114 | bool operator ==(Object other) => 115 | other is CborInt && 116 | tags.equals(other.tags) && 117 | toBigInt() == other.toBigInt(); 118 | 119 | @override 120 | BigInt toBigInt() => value; 121 | @override 122 | int toInt() => value.toInt(); 123 | 124 | @override 125 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 126 | return value; 127 | } 128 | 129 | @override 130 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 131 | return CborBigInt(value).toJsonInternal(cyclicCheck, o); 132 | } 133 | 134 | @override 135 | void encode(EncodeSink sink) { 136 | sink.addTags(tags); 137 | 138 | if (!value.isNegative) { 139 | sink.addHeaderInfo(0, Arg.bigInt(value)); 140 | } else { 141 | sink.addHeaderInfo(1, Arg.bigInt(~value)); 142 | } 143 | } 144 | } 145 | 146 | /// A CBOR datetieme encoded as seconds since epoch. 147 | abstract class CborDateTimeInt extends CborSmallInt implements CborDateTime { 148 | factory CborDateTimeInt(DateTime value, {List tags}) = 149 | _CborDateTimeIntImpl; 150 | 151 | factory CborDateTimeInt.fromSecondsSinceEpoch(int value, {List tags}) = 152 | _CborDateTimeIntImpl.fromSecondsSinceEpoch; 153 | } 154 | 155 | /// A CBOR datetieme encoded as seconds since epoch. 156 | class _CborDateTimeIntImpl extends _CborSmallIntImpl 157 | implements CborDateTimeInt { 158 | _CborDateTimeIntImpl( 159 | DateTime value, { 160 | List tags = const [CborTag.epochDateTime], 161 | }) : super( 162 | (value.millisecondsSinceEpoch / CborConstants.milliseconds).round(), 163 | tags: tags, 164 | ); 165 | 166 | const _CborDateTimeIntImpl.fromSecondsSinceEpoch( 167 | super.value, { 168 | super.tags = const [CborTag.epochDateTime], 169 | }); 170 | 171 | @override 172 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 173 | return o.parseDateTime ? toDateTime() : value; 174 | } 175 | 176 | @override 177 | DateTime toDateTime() => DateTime.fromMillisecondsSinceEpoch( 178 | value * CborConstants.milliseconds, 179 | isUtc: true, 180 | ); 181 | } 182 | -------------------------------------------------------------------------------- /lib/src/decoder/stage1.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | import 'dart:typed_data'; 10 | 11 | import 'package:cbor/cbor.dart'; 12 | import 'package:typed_data/typed_buffers.dart'; 13 | 14 | import '../utils/arg.dart'; 15 | import '../constants.dart'; 16 | 17 | import 'stage0.dart'; 18 | 19 | class Header { 20 | final int majorType; 21 | final int additionalInfo; 22 | 23 | final Uint8List dataBytes; 24 | 25 | Arg get arg { 26 | if (additionalInfo < CborAdditionalInfo.simpleValueHigh) { 27 | return Arg.int(additionalInfo); 28 | } else { 29 | switch (additionalInfo) { 30 | case CborAdditionalInfo.simpleValueHigh: 31 | return Arg.int(dataBytes.first); 32 | case CborAdditionalInfo.halfPrecisionFloat: 33 | return Arg.int(ByteData.view(dataBytes.buffer).getUint16(0)); 34 | case CborAdditionalInfo.singlePrecisionFloat: 35 | return Arg.int(ByteData.view(dataBytes.buffer).getUint32(0)); 36 | case CborAdditionalInfo.doublePrecisionFloat: 37 | var i = 38 | BigInt.from(ByteData.view(dataBytes.buffer).getUint32(0)) << 39 | CborConstants.bitsPerWord; 40 | i |= BigInt.from( 41 | ByteData.view( 42 | dataBytes.buffer, 43 | ).getUint32(CborConstants.bytesPerWord), 44 | ); 45 | return Arg.bigInt(i); 46 | 47 | case CborAdditionalInfo.breakStop: 48 | return Arg.indefiniteLength; 49 | 50 | default: 51 | throw Error(); 52 | } 53 | } 54 | } 55 | 56 | Header(this.majorType, this.additionalInfo, this.dataBytes); 57 | } 58 | 59 | Header? _readHeader(Reader reader) { 60 | final x = reader.peekUint8(); 61 | if (x == null) { 62 | return null; 63 | } 64 | 65 | final majorType = x >> CborConstants.additionalInfoByteRange; 66 | 67 | final additionalInfo = x & CborConstants.additionalInfoBitMask; 68 | 69 | final Uint8List dataBytes; 70 | if (additionalInfo < CborAdditionalInfo.simpleValueHigh || 71 | additionalInfo == CborAdditionalInfo.breakStop) { 72 | reader.readUint8(); 73 | dataBytes = Uint8List(0); 74 | } else { 75 | switch (additionalInfo) { 76 | case CborAdditionalInfo.simpleValueHigh: 77 | if (reader.length < CborConstants.two) { 78 | return null; 79 | } 80 | 81 | reader.readUint8(); 82 | dataBytes = reader.readExactBytes(CborConstants.one)!; 83 | break; 84 | case CborAdditionalInfo.halfPrecisionFloat: 85 | if (reader.length < CborConstants.three) { 86 | return null; 87 | } 88 | 89 | reader.readUint8(); 90 | dataBytes = reader.readExactBytes(CborConstants.two)!; 91 | break; 92 | case CborAdditionalInfo.singlePrecisionFloat: 93 | if (reader.length < CborConstants.five) { 94 | return null; 95 | } 96 | 97 | reader.readUint8(); 98 | dataBytes = reader.readExactBytes(CborConstants.four)!; 99 | break; 100 | case CborAdditionalInfo.doublePrecisionFloat: 101 | if (reader.length < CborConstants.nine) { 102 | return null; 103 | } 104 | 105 | reader.readUint8(); 106 | dataBytes = reader.readExactBytes(CborConstants.byteLength)!; 107 | break; 108 | default: 109 | throw CborMalformedException( 110 | 'Invalid CBOR additional info', 111 | reader.offset, 112 | ); 113 | } 114 | } 115 | return Header(majorType, additionalInfo, dataBytes); 116 | } 117 | 118 | class RawValue { 119 | final Header header; 120 | final List data; 121 | final int start; 122 | final int end; 123 | 124 | RawValue( 125 | this.header, { 126 | this.data = const [], 127 | required this.start, 128 | required this.end, 129 | }); 130 | } 131 | 132 | class _Builder { 133 | final Header header; 134 | final Reader reader; 135 | final int offset; 136 | Uint8Buffer bytes = Uint8Buffer(); 137 | 138 | _Builder(this.header, this.offset, this.reader); 139 | 140 | RawValue? poll() { 141 | if (bytes.length < header.arg.toInt()) { 142 | final read = reader.readBytes(header.arg.toInt() - bytes.length); 143 | bytes.addAll(read); 144 | } 145 | 146 | if (bytes.length < header.arg.toInt()) { 147 | return null; 148 | } 149 | 150 | return RawValue(header, data: bytes, start: offset, end: reader.offset); 151 | } 152 | } 153 | 154 | class RawSink extends ByteConversionSinkBase { 155 | final Reader _reader = Reader(); 156 | final Sink _sink; 157 | 158 | _Builder? _next; 159 | 160 | RawSink(this._sink); 161 | 162 | @override 163 | void addSlice(List chunk, int start, int end, bool isLast) { 164 | _reader.add(chunk, start, end); 165 | 166 | while (true) { 167 | if (_next != null) { 168 | final value = _next?.poll(); 169 | if (value == null) { 170 | break; 171 | } 172 | 173 | _next = null; 174 | _sink.add(value); 175 | } 176 | 177 | final offset = _reader.offset; 178 | 179 | final header = _readHeader(_reader); 180 | 181 | if (header == null) { 182 | break; 183 | } 184 | 185 | if (header.additionalInfo != CborAdditionalInfo.breakStop) { 186 | if (header.majorType == CborMajorType.byteString || 187 | header.majorType == CborMajorType.textString) { 188 | _next = _Builder(header, offset, _reader); 189 | continue; 190 | } 191 | } 192 | 193 | _sink.add(RawValue(header, start: offset, end: _reader.offset)); 194 | } 195 | 196 | if (isLast) { 197 | close(); 198 | } 199 | } 200 | 201 | @override 202 | void add(List chunk) { 203 | addSlice(chunk, 0, chunk.length, false); 204 | } 205 | 206 | @override 207 | void close() { 208 | _sink.close(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /test/filebased_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | @TestOn('vm') 8 | library; 9 | 10 | import 'dart:io'; 11 | 12 | import 'package:cbor/cbor.dart'; 13 | import 'package:test/test.dart'; 14 | 15 | void main() { 16 | final currDir = Directory.current.path; 17 | 18 | group('File based decoding', () { 19 | test('Floats -> ', () async { 20 | final f = File('$currDir/test/data/floats.cbor'); 21 | final decoded = await f.openRead().transform(cbor.decoder).single; 22 | expect( 23 | decoded, 24 | CborMap({ 25 | CborString('half'): CborFloat(0.0), 26 | CborString('single'): CborFloat(3.4028234663852886e+38), 27 | CborString('simple values'): CborList([ 28 | CborBool(true), 29 | CborBool(false), 30 | CborNull(), 31 | ]), 32 | }), 33 | ); 34 | }); 35 | 36 | test('Indefinitite string -> ', () async { 37 | final f = File('$currDir/test/data/indef_string.cbor'); 38 | final decoded = await f.openRead().transform(cbor.decoder).single; 39 | expect(decoded, CborString('Helloworld!')); 40 | }); 41 | 42 | test('Integer -> ', () async { 43 | final f = File('$currDir/test/data/integer.cbor'); 44 | final decoded = await f.openRead().transform(cbor.decoder).single; 45 | expect(decoded, CborSmallInt(42)); 46 | }); 47 | 48 | test('Map -> ', () async { 49 | final f = File('$currDir/test/data/map.cbor'); 50 | final decoded = await f.openRead().transform(cbor.decoder).single; 51 | expect( 52 | decoded, 53 | CborMap({ 54 | CborString('a key'): CborBool(false), 55 | CborString('a secret key'): CborString('42'), 56 | CborSmallInt(0): CborSmallInt(-1), 57 | }), 58 | ); 59 | }); 60 | 61 | test('Nested array -> ', () async { 62 | final f = File('$currDir/test/data/nested_array.cbor'); 63 | final decoded = await f.openRead().transform(cbor.decoder).single; 64 | expect( 65 | decoded, 66 | CborList([ 67 | CborSmallInt(1), 68 | CborSmallInt(2), 69 | CborList([ 70 | CborSmallInt(3), 71 | CborList([CborSmallInt(4), CborSmallInt(5), CborList([])]), 72 | ]), 73 | ]), 74 | ); 75 | }); 76 | 77 | test('Tagged date -> ', () async { 78 | final f = File('$currDir/test/data/tagged_date.cbor'); 79 | final decoded = await f.openRead().transform(cbor.decoder).single; 80 | expect(decoded, CborDateTimeString.fromString('2013-03-21T20:04:00Z')); 81 | }); 82 | }); 83 | 84 | group('File based encoding', () { 85 | test('Floats -> ', () async { 86 | final cmp = File('$currDir/test/data/floats.cbor'); 87 | final f = File('$currDir/test/data/floats.out'); 88 | final write = f.openWrite(); 89 | cbor.encoder 90 | .startChunkedConversion(write) 91 | .add( 92 | CborMap({ 93 | CborString('half'): CborFloat(0.0), 94 | CborString('single'): CborFloat(3.4028234663852886e+38), 95 | CborString('simple values'): CborList([ 96 | CborBool(true), 97 | CborBool(false), 98 | CborNull(), 99 | ]), 100 | }), 101 | ); 102 | await write.flush(); 103 | expect(await f.readAsBytes(), await cmp.readAsBytes()); 104 | }); 105 | 106 | test('Indefinitite string -> ', () async { 107 | final cmp = File('$currDir/test/data/indef_string.cbor'); 108 | final f = File('$currDir/test/data/indef_string.out'); 109 | final write = f.openWrite(); 110 | cbor.encoder 111 | .startChunkedConversion(write) 112 | .add(CborEncodeIndefiniteLengthString(['Hello', 'world!'])); 113 | await write.flush(); 114 | expect(await f.readAsBytes(), await cmp.readAsBytes()); 115 | }); 116 | 117 | test('Integer -> ', () async { 118 | final cmp = File('$currDir/test/data/integer.cbor'); 119 | final f = File('$currDir/test/data/integer.out'); 120 | final write = f.openWrite(); 121 | cbor.encoder.startChunkedConversion(write).add(CborSmallInt(42)); 122 | await write.flush(); 123 | expect(await f.readAsBytes(), await cmp.readAsBytes()); 124 | }); 125 | 126 | test('Map -> ', () async { 127 | final cmp = File('$currDir/test/data/map.cbor'); 128 | final f = File('$currDir/test/data/map.out'); 129 | final write = f.openWrite(); 130 | cbor.encoder 131 | .startChunkedConversion(write) 132 | .add( 133 | CborMap({ 134 | CborString('a key'): CborBool(false), 135 | CborString('a secret key'): CborString('42'), 136 | CborSmallInt(0): CborSmallInt(-1), 137 | }), 138 | ); 139 | await write.flush(); 140 | expect(await f.readAsBytes(), await cmp.readAsBytes()); 141 | }); 142 | 143 | test('Nested array -> ', () async { 144 | final cmp = File('$currDir/test/data/nested_array.cbor'); 145 | final f = File('$currDir/test/data/nested_array.out'); 146 | final write = f.openWrite(); 147 | cbor.encoder 148 | .startChunkedConversion(write) 149 | .add( 150 | CborList([ 151 | CborSmallInt(1), 152 | CborSmallInt(2), 153 | CborList([ 154 | CborSmallInt(3), 155 | CborList([CborSmallInt(4), CborSmallInt(5), CborList([])]), 156 | ]), 157 | ]), 158 | ); 159 | await write.flush(); 160 | expect(await f.readAsBytes(), await cmp.readAsBytes()); 161 | }); 162 | 163 | test('Tagged date -> ', () async { 164 | final cmp = File('$currDir/test/data/tagged_date.cbor'); 165 | final f = File('$currDir/test/data/tagged_date.out'); 166 | final write = f.openWrite(); 167 | cbor.encoder 168 | .startChunkedConversion(write) 169 | .add(CborDateTimeString.fromString('2013-03-21T20:04:00Z')); 170 | await write.flush(); 171 | expect(await f.readAsBytes(), await cmp.readAsBytes()); 172 | }); 173 | }); 174 | } 175 | -------------------------------------------------------------------------------- /lib/src/simple.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | 12 | import 'utils/utils.dart'; 13 | 14 | /// A simple encoder for CBOR. 15 | /// 16 | /// Conversion is done with [CborValue.CborValue]. 17 | /// Check the [CborValue.CborValue] documentation for supported values. 18 | /// 19 | /// If cyclic references are found while encoding, a [CborCyclicError] will 20 | /// be thrown. 21 | /// 22 | /// This encoder supports [startChunkedConversion] and can therefore be 23 | /// used as a stream transformer. 24 | class CborSimpleEncoder extends Converter> { 25 | final Object? Function(dynamic object)? _toEncodable; 26 | final bool _dateTimeEpoch; 27 | 28 | const CborSimpleEncoder({ 29 | bool dateTimeEpoch = false, 30 | Object? Function(dynamic object)? toEncodable, 31 | }) : _toEncodable = toEncodable, 32 | _dateTimeEpoch = dateTimeEpoch; 33 | 34 | @override 35 | List convert(Object? input) { 36 | return cbor.encode( 37 | CborValue( 38 | input, 39 | dateTimeEpoch: _dateTimeEpoch, 40 | toEncodable: _toEncodable, 41 | ), 42 | ); 43 | } 44 | 45 | @override 46 | Sink startChunkedConversion(Sink> sink) { 47 | return cbor.encoder 48 | .startChunkedConversion(sink) 49 | .map( 50 | (object) => CborValue( 51 | object, 52 | dateTimeEpoch: _dateTimeEpoch, 53 | toEncodable: _toEncodable, 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | /// A simple decoder for CBOR. 60 | /// 61 | /// May throw [FormatException] if the input is invalid. 62 | class CborSimpleDecoder extends Converter, Object?> { 63 | final bool _parseDateTime; 64 | final bool _decodeBase64; 65 | final bool _parseUri; 66 | 67 | /// Create a CBOR decoder. 68 | /// 69 | /// See the docs of [CborValue.toObject] to see how CBOR values translate 70 | /// into objects. 71 | /// 72 | /// This decoder supports [startChunkedConversion] and can therefore be 73 | /// used as a stream transformer. The decoder operates over a CBOR sequence 74 | /// in this mode. 75 | const CborSimpleDecoder({ 76 | bool parseDateTime = true, 77 | bool decodeBase64 = true, 78 | bool parseUri = true, 79 | }) : _parseDateTime = parseDateTime, 80 | _decodeBase64 = decodeBase64, 81 | _parseUri = parseUri; 82 | 83 | @override 84 | Object? convert(List input) { 85 | return const CborDecoder() 86 | .convert(input) 87 | .toObject( 88 | parseUri: _parseUri, 89 | parseDateTime: _parseDateTime, 90 | decodeBase64: _decodeBase64, 91 | ); 92 | } 93 | 94 | @override 95 | Sink> startChunkedConversion(Sink sink) { 96 | return const CborDecoder().startChunkedConversion( 97 | sink.map( 98 | (object) => object.toObject( 99 | parseUri: _parseUri, 100 | parseDateTime: _parseDateTime, 101 | decodeBase64: _decodeBase64, 102 | ), 103 | ), 104 | ); 105 | } 106 | } 107 | 108 | /// A simple codec for CBOR, using [CborSimpleEncoder] and [CborSimpleDecoder]. 109 | /// 110 | /// To see how CBOR values are transformed from and into objects, verify the 111 | /// documentation of both [CborValue.CborValue] and [CborValue.toObject]. 112 | class CborSimpleCodec extends Codec> { 113 | final bool _encodeDateTimeEpoch; 114 | final bool _decodeBase64; 115 | final bool _parseUri; 116 | final bool _parseDateTime; 117 | final Object? Function(dynamic object)? _toEncodable; 118 | 119 | @override 120 | CborSimpleEncoder get encoder { 121 | return CborSimpleEncoder( 122 | dateTimeEpoch: _encodeDateTimeEpoch, 123 | toEncodable: _toEncodable, 124 | ); 125 | } 126 | 127 | @override 128 | CborSimpleDecoder get decoder { 129 | return CborSimpleDecoder( 130 | decodeBase64: _decodeBase64, 131 | parseUri: _parseUri, 132 | parseDateTime: _parseDateTime, 133 | ); 134 | } 135 | 136 | /// Create a CBOR simple codec. 137 | /// 138 | /// The [toEncodable] function is used during encoding. It is invoked for 139 | /// values that are not directly encodable to a [CborValue]. The 140 | /// function must return an object that is directly encodable. The elements of 141 | /// a returned list and values of a returned map do not need to be directly 142 | /// encodable, and if they aren't, `toEncodable` will be used on them as well. 143 | /// Please notice that it is possible to cause an infinite recursive regress 144 | /// in this way, by effectively creating an infinite data structure through 145 | /// repeated call to `toEncodable`. 146 | /// 147 | /// If [toEncodable] is omitted, it defaults to a function that returns the 148 | /// result of calling `.toCbor()` on the unencodable object. 149 | const CborSimpleCodec({ 150 | bool encodeDateTimeEpoch = false, 151 | bool parseDateTime = true, 152 | bool decodeBase64 = true, 153 | bool parseUri = true, 154 | Object? Function(dynamic object)? toEncodable, 155 | }) : _decodeBase64 = decodeBase64, 156 | _encodeDateTimeEpoch = encodeDateTimeEpoch, 157 | _parseUri = parseUri, 158 | _parseDateTime = parseDateTime, 159 | _toEncodable = toEncodable; 160 | 161 | @override 162 | Object? decode( 163 | List encoded, { 164 | bool? parseDateTime, 165 | bool? decodeBase64, 166 | bool? parseUri, 167 | }) { 168 | parseDateTime ??= _parseDateTime; 169 | decodeBase64 ??= _decodeBase64; 170 | parseUri ??= _parseUri; 171 | 172 | return CborSimpleDecoder( 173 | parseDateTime: parseDateTime, 174 | parseUri: parseUri, 175 | decodeBase64: decodeBase64, 176 | ).convert(encoded); 177 | } 178 | 179 | @override 180 | List encode( 181 | Object? input, { 182 | bool? dateTimeEpoch, 183 | Object? Function(dynamic object)? toEncodable, 184 | }) { 185 | toEncodable ??= _toEncodable; 186 | dateTimeEpoch ??= _encodeDateTimeEpoch; 187 | 188 | return CborSimpleEncoder( 189 | dateTimeEpoch: dateTimeEpoch, 190 | toEncodable: toEncodable, 191 | ).convert(input); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /lib/src/value/map.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:collection/collection.dart'; 12 | 13 | import '../encoder/sink.dart'; 14 | import '../utils/arg.dart'; 15 | import 'internal.dart'; 16 | 17 | /// A CBOR map. 18 | abstract class CborMap implements Map, CborValue { 19 | CborLengthType get type; 20 | 21 | /// Create a new [CborMap] from a view of the given map. 22 | factory CborMap( 23 | Map items, { 24 | List tags, 25 | CborLengthType type, 26 | }) = _CborMapImpl; 27 | 28 | /// Create a new [CborMap] as a copy of the given map. 29 | factory CborMap.of( 30 | Map items, { 31 | List tags, 32 | CborLengthType type, 33 | }) = _CborMapImpl.of; 34 | 35 | /// Create a new [CborMap] from entries. 36 | factory CborMap.fromEntries( 37 | Iterable> entries, { 38 | List tags, 39 | CborLengthType type, 40 | }) = _CborMapImpl.fromEntries; 41 | 42 | /// Create a new [CborMap] from key and value. 43 | factory CborMap.fromIterables( 44 | Iterable key, 45 | Iterable values, { 46 | List tags, 47 | CborLengthType type, 48 | }) = _CborMapImpl.fromIterables; 49 | } 50 | 51 | class _CborMapImpl extends DelegatingMap 52 | with CborValueMixin 53 | implements CborMap { 54 | @override 55 | final List tags; 56 | 57 | @override 58 | final CborLengthType type; 59 | 60 | const _CborMapImpl( 61 | super.items, { 62 | this.tags = const [], 63 | this.type = CborLengthType.auto, 64 | }); 65 | 66 | _CborMapImpl.of( 67 | Map items, { 68 | this.tags = const [], 69 | this.type = CborLengthType.auto, 70 | }) : super(Map.of(items)); 71 | 72 | _CborMapImpl.fromEntries( 73 | Iterable> entries, { 74 | this.tags = const [], 75 | this.type = CborLengthType.auto, 76 | }) : super(Map.fromEntries(entries)); 77 | 78 | _CborMapImpl.fromIterables( 79 | Iterable keys, 80 | Iterable values, { 81 | this.tags = const [], 82 | this.type = CborLengthType.auto, 83 | }) : super(Map.fromIterables(keys, values)); 84 | 85 | @override 86 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 87 | if (!cyclicCheck.add(this)) { 88 | throw CborCyclicError(this); 89 | } 90 | 91 | final result = Map.fromEntries( 92 | entries.map( 93 | (a) => MapEntry( 94 | a.key.toObjectInternal(cyclicCheck, o), 95 | a.value.toObjectInternal(cyclicCheck, o), 96 | ), 97 | ), 98 | ); 99 | 100 | cyclicCheck.remove(this); 101 | 102 | return result; 103 | } 104 | 105 | @override 106 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 107 | if (!cyclicCheck.add(this)) { 108 | throw CborCyclicError(this); 109 | } 110 | 111 | final result = Map.fromEntries( 112 | entries.map((a) { 113 | var k = a.key.toJsonInternal(cyclicCheck, o); 114 | if (k is! String) { 115 | k = json.encode(k); 116 | } 117 | 118 | final v = a.value.toJsonInternal(cyclicCheck, o); 119 | 120 | return MapEntry(k, v); 121 | }), 122 | ); 123 | 124 | cyclicCheck.remove(this); 125 | 126 | return result; 127 | } 128 | 129 | @override 130 | void encode(EncodeSink sink) { 131 | if (type == CborLengthType.definite || 132 | (type == CborLengthType.auto && 133 | length < kCborDefiniteLengthThreshold)) { 134 | CborEncodeDefiniteLengthMap(this).encode(sink); 135 | } else { 136 | CborEncodeIndefiniteLengthMap(this).encode(sink); 137 | } 138 | } 139 | } 140 | 141 | /// Use this to force the [CborEncoder] to encode an indefinite length dictionary. 142 | /// 143 | /// This is never generated by decoder. 144 | abstract class CborEncodeIndefiniteLengthMap extends CborValue { 145 | factory CborEncodeIndefiniteLengthMap(CborMap x) = 146 | _CborEncodeIndefiniteLengthMapImpl; 147 | } 148 | 149 | class _CborEncodeIndefiniteLengthMapImpl 150 | with CborValueMixin 151 | implements CborEncodeIndefiniteLengthMap { 152 | final CborMap inner; 153 | 154 | @override 155 | List get tags => inner.tags; 156 | 157 | const _CborEncodeIndefiniteLengthMapImpl(this.inner); 158 | 159 | @override 160 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 161 | return inner.toObjectInternal(cyclicCheck, o); 162 | } 163 | 164 | @override 165 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 166 | return inner.toJsonInternal(cyclicCheck, o); 167 | } 168 | 169 | @override 170 | void encode(EncodeSink sink) { 171 | sink.addTags(tags); 172 | 173 | sink.addHeaderInfo(CborMajorType.map, Arg.indefiniteLength); 174 | 175 | sink.addToCycleCheck(inner); 176 | for (final e in inner.entries) { 177 | e.key.encode(sink); 178 | e.value.encode(sink); 179 | } 180 | sink.removeFromCycleCheck(inner); 181 | 182 | (const Break()).encode(sink); 183 | } 184 | } 185 | 186 | /// Use this to force the [CborEncoder] to encode an definite length dictionary. 187 | /// 188 | /// This is never generated by decoder. 189 | abstract class CborEncodeDefiniteLengthMap extends CborValue { 190 | factory CborEncodeDefiniteLengthMap(CborMap v) = 191 | _CborEncodeDefiniteLengthMapImpl; 192 | } 193 | 194 | class _CborEncodeDefiniteLengthMapImpl 195 | with CborValueMixin 196 | implements CborEncodeDefiniteLengthMap { 197 | final CborMap inner; 198 | 199 | @override 200 | List get tags => inner.tags; 201 | 202 | const _CborEncodeDefiniteLengthMapImpl(this.inner); 203 | 204 | @override 205 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 206 | return inner.toObjectInternal(cyclicCheck, o); 207 | } 208 | 209 | @override 210 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 211 | return inner.toJsonInternal(cyclicCheck, o); 212 | } 213 | 214 | @override 215 | void encode(EncodeSink sink) { 216 | sink.addTags(tags); 217 | 218 | sink.addHeaderInfo(CborMajorType.map, Arg.int(inner.length)); 219 | 220 | sink.addToCycleCheck(inner); 221 | for (final e in inner.entries) { 222 | e.key.encode(sink); 223 | e.value.encode(sink); 224 | } 225 | sink.removeFromCycleCheck(inner); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /lib/src/value/double.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:collection/collection.dart'; 10 | import 'package:ieee754/ieee754.dart'; 11 | 12 | import '../constants.dart'; 13 | import '../encoder/sink.dart'; 14 | import 'internal.dart'; 15 | 16 | /// Enumeration to allow the user to select which level of precision to 17 | /// use encoding a CBOR float. Defaults to 'automatic' 18 | enum CborFloatPrecision { automatic, half, float, double } 19 | 20 | /// A CBOR float. 21 | /// 22 | /// Encoded to the least precision which can represent the value losslessly unless 23 | /// the user has selected half, float or double precision. 24 | /// If the user has selected the precision to use and the supplied value cannot 25 | /// be encoded in that precision then an [ArgumentError] is thrown. 26 | abstract class CborFloat extends CborValue { 27 | CborFloatPrecision precision = CborFloatPrecision.automatic; 28 | 29 | double get value; 30 | 31 | factory CborFloat(double value, {List tags}) = _CborFloatImpl; 32 | 33 | /// Set half precision 34 | void halfPrecision(); 35 | 36 | /// Set float(normal) precision 37 | void floatPrecision(); 38 | 39 | /// Set double precision 40 | void doublePrecision(); 41 | } 42 | 43 | class _CborFloatImpl with CborValueMixin implements CborFloat { 44 | @override 45 | final double value; 46 | 47 | @override 48 | final List tags; 49 | 50 | @override 51 | var precision = CborFloatPrecision.automatic; 52 | 53 | @override 54 | int get hashCode => Object.hash(value, Object.hashAll(tags)); 55 | 56 | _CborFloatImpl(this.value, {this.tags = const []}); 57 | 58 | @override 59 | String toString() => value.toString(); 60 | 61 | @override 62 | bool operator ==(Object other) => 63 | other is CborFloat && tags.equals(other.tags) && value == other.value; 64 | 65 | @override 66 | void encode(EncodeSink sink) { 67 | sink.addTags(tags); 68 | 69 | if (value.isNaN) { 70 | // Any bit pattern can be returned from `toFloat16Bytes()` as long as 71 | // it is NaN, so it's a good idea to make sure this is consistent. 72 | // 73 | // This value seems to be ideal as it is the value used in canonical 74 | // format 75 | // 76 | // https://datatracker.ietf.org/doc/html/rfc7049#page-27 77 | sink.add([0xf9, 0x7e, 0x00]); 78 | 79 | return; 80 | } 81 | 82 | final parts = FloatParts.fromDouble(value); 83 | 84 | // Automatic(default) conversion picks the best encoding. 85 | if (precision == CborFloatPrecision.automatic) { 86 | if (parts.isFloat16Lossless) { 87 | sink.addHeader( 88 | CborMajorType.simpleFloat, 89 | CborAdditionalInfo.halfPrecisionFloat, 90 | ); 91 | 92 | sink.add(parts.toFloat16Bytes()); 93 | } else if (parts.isFloat32Lossless) { 94 | sink.addHeader( 95 | CborMajorType.simpleFloat, 96 | CborAdditionalInfo.singlePrecisionFloat, 97 | ); 98 | 99 | sink.add(parts.toFloat32Bytes()); 100 | } else { 101 | sink.addHeader( 102 | CborMajorType.simpleFloat, 103 | CborAdditionalInfo.doublePrecisionFloat, 104 | ); 105 | 106 | sink.add(parts.toFloat64Bytes()); 107 | } 108 | } else { 109 | switch (precision) { 110 | case CborFloatPrecision.half: 111 | if (parts.isFloat16Lossless) { 112 | sink.addHeader( 113 | CborMajorType.simpleFloat, 114 | CborAdditionalInfo.halfPrecisionFloat, 115 | ); 116 | sink.add(parts.toFloat16Bytes()); 117 | } else { 118 | // Invalid conversion 119 | throw ArgumentError( 120 | precision, 121 | 'Cannot encode value as a half precision float', 122 | ); 123 | } 124 | break; 125 | case CborFloatPrecision.float: 126 | if (parts.isFloat32Lossless) { 127 | sink.addHeader( 128 | CborMajorType.simpleFloat, 129 | CborAdditionalInfo.singlePrecisionFloat, 130 | ); 131 | sink.add(parts.toFloat32Bytes()); 132 | } else { 133 | // Invalid conversion 134 | throw ArgumentError( 135 | precision, 136 | 'Cannot encode value as a normal(32 bit) precision float', 137 | ); 138 | } 139 | break; 140 | case CborFloatPrecision.double: 141 | if (parts.isFloat64Lossless) { 142 | sink.addHeader( 143 | CborMajorType.simpleFloat, 144 | CborAdditionalInfo.doublePrecisionFloat, 145 | ); 146 | sink.add(parts.toFloat64Bytes()); 147 | } else { 148 | // Invalid conversion 149 | throw ArgumentError( 150 | precision, 151 | 'Cannot encode value as a double precision float', 152 | ); 153 | } 154 | break; 155 | case CborFloatPrecision.automatic: 156 | // Nothing to do 157 | } 158 | } 159 | } 160 | 161 | @override 162 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 163 | return value; 164 | } 165 | 166 | @override 167 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 168 | return value.isFinite ? value : o.substituteValue; 169 | } 170 | 171 | /// Select double precision encoding 172 | @override 173 | void doublePrecision() => precision = CborFloatPrecision.double; 174 | 175 | /// Select float precision encoding 176 | @override 177 | void floatPrecision() => precision = CborFloatPrecision.float; 178 | 179 | /// Select half precision encoding 180 | @override 181 | void halfPrecision() => precision = CborFloatPrecision.half; 182 | } 183 | 184 | /// A CBOR date time encoded as seconds since epoch in a float. 185 | abstract class CborDateTimeFloat extends CborFloat implements CborDateTime { 186 | factory CborDateTimeFloat.fromSecondsSinceEpoch( 187 | double amount, { 188 | List tags, 189 | }) = _CborDateTimeFloatImpl.fromSecondsSinceEpoch; 190 | 191 | factory CborDateTimeFloat(DateTime value, {List tags}) = 192 | _CborDateTimeFloatImpl; 193 | } 194 | 195 | class _CborDateTimeFloatImpl extends _CborFloatImpl 196 | implements CborDateTimeFloat { 197 | _CborDateTimeFloatImpl.fromSecondsSinceEpoch( 198 | super.amount, { 199 | super.tags = const [CborTag.epochDateTime], 200 | }); 201 | 202 | _CborDateTimeFloatImpl( 203 | DateTime value, { 204 | List tags = const [CborTag.epochDateTime], 205 | }) : super( 206 | value.millisecondsSinceEpoch / CborConstants.milliseconds, 207 | tags: tags, 208 | ); 209 | 210 | @override 211 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 212 | return o.parseDateTime ? toDateTime() : value; 213 | } 214 | 215 | @override 216 | DateTime toDateTime() { 217 | return DateTime.fromMillisecondsSinceEpoch( 218 | (value * 1000).round(), 219 | isUtc: true, 220 | ); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /test/issues/issue75/long-decode-file.txt: -------------------------------------------------------------------------------- 1 | omNtc2dZF41EABi9b3lXd8Z24woF1esjQizRWlww83Nde4FxbuKybFe3Gu+YBycfr2GfAvX3QfDpjXMjHcTLuu952s3zFCuGGYTRCZLKiYq3wHIwlA9B1Nxln1Byr9bHFPlMrVzwBgBUCUuAYF8M0NGjeh8/z+QJm+5+1OHdD9Aq7AtbQzIq3aC/BIZfN2iZYjVB1RmQhsRS/ZsLwN1LEup27lCCof/4q83icfelhYAO4H6OOHo+Zc4nxLlcni/ZBuP+uIC/GYuZGS5fVX62cMX0BkPnrKytBT38f5juOLzIj14cmg9rwCfRWNI0WikH8JmrQ8i++UCql76UFHDSrM1uPfWharrmQzIwNWFgYOdvmK+GPrfTD54lEH0IE/2mcKrpXFE6J6UJzak4vbTLNmXFMA9pkEXpr8plJCzh6s/AlHuthb65RykF+2OqxLXVg1ja9pOBa3Mqjp/RAIbtZ9SkjIeknaagLpW8wuz0NOUdKaKjylZb3aJCds/lCmjTtWfmvgoQbqfi0l/9xkM1cYcJlnxRUMAMkjfzheZDBiP+Ig3WrRpj14Ab3QIScasKY2+oRlKQ6CbYTrDPREtd845LBNb7FfrXaZ/wI3jMYqYyR95QHMEvU10l044/Ue5RsTrhpgTjetiuqB4pdp7icOVP09exm+n5kqbfAAnSqzvHDwKbg96Pej80wDzbRHeNN9tNOYOGE5zefVypHX32sciRoGeVCa//dtpWAAtXApev5Xo7CO7x789PG/JEL15LqBUC9zCerN/z1TNSSzk2qaHVAvkBfsFiucGMGYNEnrls3zHk8dFFPnVfrO9srKBuEKv2QogSEQWqCLRBU1wYH/iClUokG3DmEPjB6hdYrl0isnSMBTuflzJNRyaGmNgkn3ENw+OBDXogNtIUAf62Rq7obTRnaATcoOFU1PkcovgiVVKAyb4hOrWZzWJepPNMG5Bx85PCRfMbpl2oFP2on4aY4A2tgvXC/+YztBl8XNuI/BQ8CJ9Mi5knoG5ZmRg3e1EHmpwIVUI/7sRbacOP1ipbmmrSRELrdxdhwLAPoHyyI3Toz04sMy9LlTjwpxUPlE2sYhoTxXQu3cUcMYW6+tLVgEu90/M2beAIa5O/88/Omg3aCw6zdm5Kotz7+tlYGh6JF+gOFEWYqaml/8uYx2+CU61w+LbgsdZkdijNBmosjOr0GramrzzQE233XPmxBFW1JACLUZbdg5p3AyehtrflLutWxsNdpjhrBH4fn/jjgkM2Uwkq4jGorCGxUvHXBN70UrcbS6bfj1Fl1leyWWkPBsBNGfycnihRpIAe++PkQv2b2UOHxDI69nWl/vB3iW3A54UgMCJMvekGpMeVOWqkbgfLbXUCEzfgDnaDE5AwVKcvpTC53rlyuvFNeB1JwOgZPjNtfVk/98XI0+FevyOYLY/I3MK7ocGRM8Pf1vauQLVUDW9Jdb6y0uz+ci+m1YRK+ENbugxAfuiHXPSyvFAMvSna8PywRAq05xvknlV+QlvtNb3bcjMFLk1lfC7Zc+MKxs6RoDF5zVdpw8xFyeeZzo59X08C74x+zSPmNlTNOrdGJFvipxxegup8AC1EG92o1WHwPR8/faw9jT/7X6S/lf8SBqz8vDnd1BCqnpFrPDOmzPUCdv9XvLQ24aGhOEZqtPGP4ZTdAZp6nPNXXc+u7WY3yhLdAwTYoefdoEt05d+/za5njCBz7+UvjMkU4bzywzNnUUGVjilYytklJQwIOxiyGmL50Bhn73xvHlNQvrDscL3q3nJB3bvM4AFFcYctAua1GZmoL96GYv3NJIhkt0XPeknD5l18tbWUbRIiAUdTxjSa+YyuZRtUMHkD2Rgpe3DJ4y0gnhNIdvQ8BMaMcXpjnVTYPQdI7+gNPqg3RuCS7LNoSJfwOBANw61hiCZfFcQM6Wnkjn4sjAwykMBTFvmnJSrBaiPAPKI9hIB56f5IId5woCYZf+KVf3o0lU19Mc9okGKRmQ5BPT+EPD8vBAN4U37KVR5+OGob/nuS8cLfzaDojzRT1IKzuw6HE2DyEnZa01fx+CkvvpVEDB27CGSQtSJldu7tjvCepsEgfPr5PUIMAkohVPzr3XMq9x49B6knq/OP/pKLkReGQrHWXt9001JN7eERD4rGrXsRQnFBZuuy/gSRcKn/Me4/7N3nFM489qsroyFaubf9iBHCX9VH67B46ptpDM2Zf/Nu5H5w4M33Y0iYfV3s9REmbrxBmYrj8Ikfc51p5+FKeNBnIU5ndOPiJQoni3EeIFrO7zyiySZ5e0kRYcXO+p2j3FgZhQ9nqpB3kmWqFTOSz7gANyBvYWLZTAuwszwoTp3ijyztZAp0LQlGRQqrm5JzLMVgdSmbLNVu/r1wBkhbnwJlZJ6SEqHzo+9SjuH8DzbvVWepdVZOb/k9QrRRxzk8wHDvHD8a5UNARt3XlWJUDWPsuZ1Wacf6Qr6MXUugT35oHxXTnfr2ZSPsHGUmdnuL9uPtQwMnONpZm+ZoZdzL7GT5KfQKsA7hFe2jEENkDIHAUvuYHBKFJ4DjRwmvqjbyFBulxyTl1L1io4CB0eBXXzI0I8kHm02Y7cfzC+j1WoAPjdW0azI7AN/uGb+eiYWRGQzkFa9kMvcSvHQi/ozjqk1DhtfmtO8bri3EMDfZONkeLRZ9dM8sZieWMF7u33rsROV3UkwO10o1pje0KVXYlOZ8JtkI2gto1pF6JF+eQX1YQkW6V5vT7d7x7GurOIu1nSF2zR7/GG1xRjYuEsJ42jU3Mjvv3RumymVFzKjO7h4HwAt9KBaW20wrCkR5d5Jpe4Mp8ut9FQGQSfggRoH2nwZg0rq0ZuVyK+hQyB6OE/cTG5E74g19+kJQKBqZ6QY5V3fzoC1hyB4YvpVBAW07mGtKN6PZPopDaB3R0s6Tkin5Y5dMi+PCjDdsGm9rx5INczmBuallWbdqpezSxxeY0BBua7DuFfrSAcLnUAZmSK5mlAMDiuPBVdts0fBX7GdudvD0RvbWz0IDuC+fSZxFkstyL1Xw1KO7uGekRj2ptO0Zx2DzQzspuWDDcdzEw5bNv3RBYTOn32d9p72U3mjxz8zCxd1QCUiMf8gH/SfUOinDgyQEiG7AIdwd5LoM1nyug1jD3a4evHJkpog5b4Vae4krCZFohZF2qPytug5MKVxTIa3muCEZFYw4goxr8vuN5ShSmaGvKICtr7h0qZIYjB1PphX26RcmE/wl7wiOsP3H5z0Bb7slm4fZthGxZYIxIwPFoWN4q7kHCnvubRt9SxZPJeUjteqqJAQcaJGWUj6I/xEVGmLPvsGwjTaNy1Q1jCWWbCvBEVGF6KyxK8k/jWWXCYRAgWw5kFUCC0eW34fY7+AS8nUlYruXJ7zlQGj/zcHHfnPeqkI/8WzRAOSO9RsrZVPxgC9jqEBLORwgC3a7nEu31oGHnzwK/pclL5B3jUoQ/lNkwQFoV9zSI+Mwp0ViNmMycca4O3fXraX4IUdIxjEcPTbEVpgs6J4rSFvfRbAJm++3nuLFrSv6lgIgYDXsYpzAY8dQVRkiw1QZozdzvzON/5PgXsfqUVCP2LZNUp8GCz94kp6IC4+8v2N0BmqR+XfkrG/eXLEGEhQPNNkL2ZNYQlOvuYG0OWNda6i3qDFIFhlrLP7HURL3D7GTGVqi/tzsD5bxWwP/lRV5Mf+/WPpJIex5hZfQdSQWCNAjWKO0q7FZkUaP9SwKeoFKGpE70bzimlUxOlZJ5O822KqZPIfZQ4AaSinxNVqg7pm51Wexmipt80wUTPi+3nQuEdqmwGrdPhRivXZ/0vSxuKps8Z475tCd3EK6zZSAqVaULV9oCypSWQAGxqaJQuJ3EZXUFaIaCNoqJZisLjDXnjk7CqOCaLTKTtiEkW5TH7VjTGSywKe9lZkJLwPm01GFcZIIRrZkNOEfGKnNsdh5zfHclQcNhqADY/gvmKQBL1e0NNt7URVsTCXYaqSl6F5WDoh/wbRXq9y3U4+VlAPXrPTDloKHXQe7ma1f7gHGJpXDoieqRN/QPU7bSOD9ZFpbYfvjC5wjKDTPi1Ls3CaoSZKIx8TssD/TUDhv0WEtCJess+g6GIKye9Ms2iyZt05ucGBRNPjoYj+egntLGzfuV0vjPAuXIRtY0AyYMNtiDZcQ3+C1/m/3nmQ35dLwNt2ZbxhidmofFrc/mnm/5IWfC8WZ6sQ3RUOKPwLywtNFsiFEUamMe6/bpv7OirsDUMILVMjbXbTNSOxfLCehzedogTp0xRd2YGNOjSuWXPKL5xeyBgEz6y529LdHGRPnFqB0hPqQlIOUWXM2V45QvshRaP864aJ5wLXmUxQfVY60uQplaSlZ2+yeQac9NP3YhVO5QLaGx3wExSa140XLdtOTaIRlR1C6s0zjDrzwayjq748lGbAi9czQqW9n9x464fN8GOM0TAaSzpE/Fk1ocZ/XAEJYSYYCZP5vU+h0pPVuxGKguEiGG+ZUdeFmvRPn2Jc4lwimfTs9XV+YGTgYGDDTvzOz7XgIbQdxblX79eyzM55arLK5XNdHDkk6Bc2R/M8pZt6/m6wVYDNNapMF9bD0TNq03KGMo5ZgMSfoPlCD7xOVDbY4SbnKRZ3tWbi1R1aKnKCnt9qfbj/dSfeP4dTKS2K9wBCem9/pC2TRomKoGDTxSl1U5E/1ToCpEFYA2ZNGPRgxcaZ9NzwXjbLbHhVcU/DCfrMfy93mi83yx0roCJDtgCogDggujvgen8EDKPJ4mxcrz8bOtouy1EVNqWVDb96rd1XY7P48jceSFMNIn60sqzLy3E81kd6h6s4jgCclAvQe8xF6mOPiR3OkWH83Y9VzJeXusaAwvMGqWxwxBt2UDPvjVreVhjzoV8VE92125ika9IvpThlXQPgqZ5hVNk9YSLT64KLF3erGD6iBTJ8yxQelZrJAlgKWWZzD1+5JoyjVU5RYP+eS5zUYCzLrGbfB/tsqN/tdZj4E7uO1ZHdu9pPlesF/njDHcKTUR0asT4DcCwSmWGtNVPmbS3gp0cB3oBIgnQ6Vt9yEaIrlLoQp2idsPJKVqdU8cr3S+wGd9oJzUm8aUB22BXQiP6YtH4c5m0G5G/zQrd/Odp88nV1C+LVyc81n4Yl0ae9P0mZtlK6pbcG65m0H2E+H9Kl31TIzmHI90y+cuJ9bIPbhBxdd8+KW/seYr/PTt2JiOtxQyxsUZdyVRByFM/sAuskWRFq6u0ogJbJMBBcKz8BgO/85M6Vc1apHw2rq0BUwqnJlf/w0OuZx1JbhdyXfQzuBN4Y5IrhabTMATWfSB6vrFvBNi+m4gzl7mT56i+jITEkIaY8f/TcOe//hfva1aTRAHlx8l74Xesj+ggVa5LWIfawMWN5ca/QDuyB6WxajB3WTjlPiYQTq+m2Eq+QQSjseMudJWw1rg7GcDZ4kDr4PeVSBJ60MiGTymONivv3tt21WRfDp637Mf2zfiAtbeWe7IudPN4d6mKPM9VbLSj0QZgPTN8rNIM5ZqMPx+S/YcacdI/v8n5hQ7zr2VxxQYJDuI4nCtgY8o8XaLnJsFF8K473m+2BpepCeDcQBLv9GCUSNKidDokhXNB2l5P/p1Gp8Upw7U6tCC5DqxcL8vEeG0BnyZs6bwoRLK+uNLMxY5DA3AYrkgM4IASc9quhHOpZuH59ReIpQ0ijvhS13xYu9GN4FneMtg1KZsDhLiAXeMSVlzQeQLLs44qprr74HuJAEmrEKXAY7OeaDSTUsFRuXwnWCsGjeGFmeh6zwX8nj1ZWP+LH4/heAkeqMQRJDCL9MxkwcafdAqWExgN+VohwMozI3eFCtDvXtB4x5XF8usZ80qkwdBIplbAckYITpKDnR69mtK3EqxNEH+r+uwitfRPGFME1ChKH4ieme7Y4cF1veDi2NBdCNq4wFuuBkHO35t9ZFK3jKLphbQ96a6op0pvKLmZC8XE4zbYN+k6SlQnZw2CH5/CMn5LfumEne14axmSR75BJ9pDXUZP6tAS76KK5y51OgklEDUMZo4WNkUl+uqDoYtdMGCoZiM6K/yo5Uf0QbxtSUrmu70dHTdXFW7pzOUecf9Hz615Jhp075w81XHY2Yrm1pQEyAyZ+4mH20B7WDKjnGITEJ248ZsBtFUK+5Y+RLb9rtXvPdykaMrVpUzxdL8IZ7/fz//A6pjbsiPFA3wGBtgRmsN8MEjZzqTELAahI255m9nPpdWToUjlouRZBFh+3Zoap331xj3ZUzSdRjjUkjXqUty72RmIKOW9Ax8EJd3KyoC9Akc95Zx71idkWsyqRvSMVotiuW3Z9GLN13ojcKy+xPyrNabT51DLtbQ2D4qwyP1Jms9Ep25SW+p4pEO2dCtPx1UtxtZLhV2DHlSS7Ci2ckQN0KA6xV8tfjrMQ9enS9w6Xi1gIxbPWQR6HbSu97fn02oIW7bfBfgjUS2DWxOuIgVT/eyQhqTPQLDhWATcy5SwwgT0KKvb6nadmxoTmiBAMLgbz+qrpcAo34y3xFRZntLY+5L7dUNfGEFUeFH4RBQG2j7n/F/KTFZtXDL938Dd3A1kwMt6bVoqP4TOGxN5xRO+hBf56/THqbu6xer7dO12nxFMtWWRlwmfjZ5YMulJHYNgy8YoNRg9CC8PozuuR45nEf1cZgsggrR2QACwP+3gHO2nr0Osdq5BIjL8Pqc/EqMDDtKdfWQyyNlIBZ9hCZxswXOE+GIikWf9JS3ySbqDdjp0LcG4VNCyO5JgZJKiJBMhmtg9tENuJfRcKmNcQpNVn9nP8Hqn9xxD+EMfEFrUewsNbXDbKJ941/RYwln/Klxjjc8RBgybiM5Yi61Q4lSEI2dVP3oeS3u4ig+pU6xE/a3pnfbtHPXxhT81i8Jf9m0fVd2RjVUE4VQnMt7rU8vp5AslDmB3EjlcBm0rnc78RsCFlmeB1StXC8o3blAdoyjgz0v10eFrVuBIjnKuOiVS1rEfcyFcwdb+EThzdLhgs9cEmUqCpMo0nAb3TEhOevB7J+fjETy52j88Hd5yBBrlat+qGfWC/u6DMLL8+KBwSFOS3LyPx3Vjz7JY+labRbwUEWhRMW580kLJBKS6QuhVgz6AhpvpNiT/UDGIyvysUj1nxfIHsQH/rwRP06QsiF1IyCJYjbFxYENMn1bhYnIt3/VGu2dEIe+rfB4ILM2JLa6EWJXbwi+eRtBniEeeeVSYy+YylGlLmBmtO00oZspsZS18xSb9KSuqq2KMKnYvS/Vcme5nLpDHrJ6BO3kDY+aphSnsermqhmSFtIEROF9iSxHq043kJR3NCAjZiwxWWfTER+x20LaNPPUyyi7je15Lptvl/ggl6YamP45nX6Fda8B08bTPL+cWQJ8JrxaXoCqOek3qaCB8Rkfz3iV65aK75N23HUekIDPX8L67GyxSu3J/w79+/iLTekarQOrrvKj9t+zZx25HvsBDEuHRCvLXYAoAz1Zl2d97/ai4CGKe2xxtVH0+kPcP6LuYdFPqxvOFeTTnWMK4nLb9zzWetC656EgbbBxhMfVGRpkiL/zNfK14V1Qdry35JEWS1hUn1sOh+O3bROfeKvkDlH8d0VoHNmjLuvXa02x7u1anWdaSB2IDLKeFbc6oJt9GW9KqR7SadkmMahakQ87ThLq94Hy5JSZcGEVW0g9wiGiHzF83S/EYwjJLBI/yaatfrWmH6an77nmkYDTEQbne+ych0rePzWCK2zX7+/AKkQiuzW/a9HHoaNcDLnNXEKxakibzDeXqygM8h/IxDu7w3/KRg85J8xUXzzZjzLdRExW+uvLWn3MFdqilW2LqJ0xUizpRA5nrEjS/MBFjCNZvdicy4DjQNyBOzcLGNcJuyNc5vJzPfxfnOTJ/9aZoY4xTiYRRYAyZuhzkakNS9Mp9xjDH20YC72jXSzcGnKEt8rkPXGo4OlWI0SqmsU+Cszd1/yfo+Tr9PnHZTCkRyAU2OQdWeiEouqY+sKdopQeCDK0pzPTR/i2WIIKL8cWlyRRV7SHySKPpuUCVcUPqdZ5hNXGO2ri1USvQ9DR6l9zxLbImVub25jZVgY9pi4A/lYK0IOTFo4MZTyDQAjCMM4HnjQ -------------------------------------------------------------------------------- /sbom.spdx: -------------------------------------------------------------------------------- 1 | SPDXVersion: SPDX-2.2 2 | DataLicense: CC0-1.0 3 | SPDXID: SPDXRef-DOCUMENT 4 | DocumentName: cbor 5 | DocumentNamespace: https://pub.dev/packages/cbor 6 | LicenseListVersion: 3.8 7 | Creator: Tool: https://pub.dev/packages/sbom 8 | Created: 2025-05-04T09:14:10Z 9 | CreatorComment: Please refer to the AUTHORS file for contributor/creation details 10 | 11 | 12 | PackageName: cbor 13 | SPDXID: SPDXRef-Package-cbor 14 | PackageVersion: 6.3.7 15 | PackageFileName: cbor 16 | PackageSupplier: NOASSERTION 17 | PackageOriginator: NOASSERTION 18 | PackageDownloadLocation: https://pub.dev/packages/cbor 19 | FilesAnalyzed: true 20 | PackageVerificationCode: 8406c61324d69ba1a901b9d012876389061b19b5 21 | PackageHomePage: https://pub.dev/packages/cbor 22 | PackageLicenseConcluded: MIT 23 | PackageLicenseInfoFromFiles: NOASSERTION 24 | PackageLicenseDeclared: NOASSERTION 25 | PackageCopyrightText: NOASSERTION 26 | PackageSummary: A CBOR library for Dart. An RFC8949 compliant encoding/decoding CBOR implementation. 27 | 28 | 29 | FileName: lib/cbor.dart 30 | SPDXID: SPDXRef-cbor-0 31 | FileType: SOURCE 32 | FileChecksum: SHA1: c63cddb51fe5a3d4f1a2282ad1be626d64f5f55e 33 | LicenseConcluded: NOASSERTION 34 | LicenseInfoInFile: NOASSERTION 35 | FileCopyrightText: NOASSERTION 36 | 37 | 38 | FileName: lib/simple.dart 39 | SPDXID: SPDXRef-simple-1 40 | FileType: SOURCE 41 | FileChecksum: SHA1: 99dae2e2449de60075386b6966b2a89fde97f0f4 42 | LicenseConcluded: NOASSERTION 43 | LicenseInfoInFile: NOASSERTION 44 | FileCopyrightText: NOASSERTION 45 | 46 | 47 | FileName: lib/src/codec.dart 48 | SPDXID: SPDXRef-codec-2 49 | FileType: SOURCE 50 | FileChecksum: SHA1: f1c73e4b308892fcf4a20fce38f5d9e84d9cd892 51 | LicenseConcluded: NOASSERTION 52 | LicenseInfoInFile: NOASSERTION 53 | FileCopyrightText: NOASSERTION 54 | 55 | 56 | FileName: lib/src/constants.dart 57 | SPDXID: SPDXRef-constants-3 58 | FileType: SOURCE 59 | FileChecksum: SHA1: 6039424185929b87cad99fc5ae8c0fafc96495f9 60 | LicenseConcluded: NOASSERTION 61 | LicenseInfoInFile: NOASSERTION 62 | FileCopyrightText: NOASSERTION 63 | 64 | 65 | FileName: lib/src/decoder/decoder.dart 66 | SPDXID: SPDXRef-decoder-4 67 | FileType: SOURCE 68 | FileChecksum: SHA1: 3a3acb7b56bc62ac0489de4abe838c2e728a4171 69 | LicenseConcluded: NOASSERTION 70 | LicenseInfoInFile: NOASSERTION 71 | FileCopyrightText: NOASSERTION 72 | 73 | 74 | FileName: lib/src/decoder/pretty_print.dart 75 | SPDXID: SPDXRef-pretty-print-5 76 | FileType: SOURCE 77 | FileChecksum: SHA1: 243573ed26f445cd07a3e84384cc4a329e0a17c5 78 | LicenseConcluded: NOASSERTION 79 | LicenseInfoInFile: NOASSERTION 80 | FileCopyrightText: NOASSERTION 81 | 82 | 83 | FileName: lib/src/decoder/stage0.dart 84 | SPDXID: SPDXRef-stage0-6 85 | FileType: SOURCE 86 | FileChecksum: SHA1: 681b38fdcfce87d02238108876cb011b1ac441ed 87 | LicenseConcluded: NOASSERTION 88 | LicenseInfoInFile: NOASSERTION 89 | FileCopyrightText: NOASSERTION 90 | 91 | 92 | FileName: lib/src/decoder/stage1.dart 93 | SPDXID: SPDXRef-stage1-7 94 | FileType: SOURCE 95 | FileChecksum: SHA1: 8f961f3aece8b7ce9dbe740b8d498b6c111a16ef 96 | LicenseConcluded: NOASSERTION 97 | LicenseInfoInFile: NOASSERTION 98 | FileCopyrightText: NOASSERTION 99 | 100 | 101 | FileName: lib/src/decoder/stage2.dart 102 | SPDXID: SPDXRef-stage2-8 103 | FileType: SOURCE 104 | FileChecksum: SHA1: f114d11c1469580e851c798b68ca37cbc4828990 105 | LicenseConcluded: NOASSERTION 106 | LicenseInfoInFile: NOASSERTION 107 | FileCopyrightText: NOASSERTION 108 | 109 | 110 | FileName: lib/src/decoder/stage3.dart 111 | SPDXID: SPDXRef-stage3-9 112 | FileType: SOURCE 113 | FileChecksum: SHA1: 5e0192fd559aa43030d7fedc6e5affefa85dadbb 114 | LicenseConcluded: NOASSERTION 115 | LicenseInfoInFile: NOASSERTION 116 | FileCopyrightText: NOASSERTION 117 | 118 | 119 | FileName: lib/src/encoder/encoder.dart 120 | SPDXID: SPDXRef-encoder-10 121 | FileType: SOURCE 122 | FileChecksum: SHA1: 82e9c82259d60d80ac65e1b6d79385e25c50e682 123 | LicenseConcluded: NOASSERTION 124 | LicenseInfoInFile: NOASSERTION 125 | FileCopyrightText: NOASSERTION 126 | 127 | 128 | FileName: lib/src/encoder/sink.dart 129 | SPDXID: SPDXRef-sink-11 130 | FileType: SOURCE 131 | FileChecksum: SHA1: 8d6df2e47d69a0d65d70dcda7abc3e69eb2928aa 132 | LicenseConcluded: NOASSERTION 133 | LicenseInfoInFile: NOASSERTION 134 | FileCopyrightText: NOASSERTION 135 | 136 | 137 | FileName: lib/src/error.dart 138 | SPDXID: SPDXRef-error-12 139 | FileType: SOURCE 140 | FileChecksum: SHA1: 54d5d10de3972e1c5af7ab6fbd2d7b057facfa2b 141 | LicenseConcluded: NOASSERTION 142 | LicenseInfoInFile: NOASSERTION 143 | FileCopyrightText: NOASSERTION 144 | 145 | 146 | FileName: lib/src/json.dart 147 | SPDXID: SPDXRef-json-13 148 | FileType: SOURCE 149 | FileChecksum: SHA1: 7617a2e355344b4a5e7a72e048a17fcc6eb8d25e 150 | LicenseConcluded: NOASSERTION 151 | LicenseInfoInFile: NOASSERTION 152 | FileCopyrightText: NOASSERTION 153 | 154 | 155 | FileName: lib/src/simple.dart 156 | SPDXID: SPDXRef-simple-14 157 | FileType: SOURCE 158 | FileChecksum: SHA1: ca4610bc1740921f77c4bc1ce428b1447cba77fb 159 | LicenseConcluded: NOASSERTION 160 | LicenseInfoInFile: NOASSERTION 161 | FileCopyrightText: NOASSERTION 162 | 163 | 164 | FileName: lib/src/utils/arg.dart 165 | SPDXID: SPDXRef-arg-15 166 | FileType: SOURCE 167 | FileChecksum: SHA1: c090d3ad1a296fefee25d7a0fb20d68aa0153ce5 168 | LicenseConcluded: NOASSERTION 169 | LicenseInfoInFile: NOASSERTION 170 | FileCopyrightText: NOASSERTION 171 | 172 | 173 | FileName: lib/src/utils/utils.dart 174 | SPDXID: SPDXRef-utils-16 175 | FileType: SOURCE 176 | FileChecksum: SHA1: bc1ddedd78422ad638b21ebf24386475eb93c3bc 177 | LicenseConcluded: NOASSERTION 178 | LicenseInfoInFile: NOASSERTION 179 | FileCopyrightText: NOASSERTION 180 | 181 | 182 | FileName: lib/src/value/bytes.dart 183 | SPDXID: SPDXRef-bytes-17 184 | FileType: SOURCE 185 | FileChecksum: SHA1: 909cd8eb207dbd803478e0fad37e951fba2e10fd 186 | LicenseConcluded: NOASSERTION 187 | LicenseInfoInFile: NOASSERTION 188 | FileCopyrightText: NOASSERTION 189 | 190 | 191 | FileName: lib/src/value/double.dart 192 | SPDXID: SPDXRef-double-18 193 | FileType: SOURCE 194 | FileChecksum: SHA1: 36e443e6de74de7531fcbc6255aa21d3b89e90b5 195 | LicenseConcluded: NOASSERTION 196 | LicenseInfoInFile: NOASSERTION 197 | FileCopyrightText: NOASSERTION 198 | 199 | 200 | FileName: lib/src/value/int.dart 201 | SPDXID: SPDXRef-int-19 202 | FileType: SOURCE 203 | FileChecksum: SHA1: 75965d7fcdb22ac47e6b6f14da059174edeba1dc 204 | LicenseConcluded: NOASSERTION 205 | LicenseInfoInFile: NOASSERTION 206 | FileCopyrightText: NOASSERTION 207 | 208 | 209 | FileName: lib/src/value/internal.dart 210 | SPDXID: SPDXRef-internal-20 211 | FileType: SOURCE 212 | FileChecksum: SHA1: e19d7a112c42222fa9a85786113b84c6089226da 213 | LicenseConcluded: NOASSERTION 214 | LicenseInfoInFile: NOASSERTION 215 | FileCopyrightText: NOASSERTION 216 | 217 | 218 | FileName: lib/src/value/list.dart 219 | SPDXID: SPDXRef-list-21 220 | FileType: SOURCE 221 | FileChecksum: SHA1: 23a83ef827b0584f46b82d1147fa7f0eddf5a930 222 | LicenseConcluded: NOASSERTION 223 | LicenseInfoInFile: NOASSERTION 224 | FileCopyrightText: NOASSERTION 225 | 226 | 227 | FileName: lib/src/value/map.dart 228 | SPDXID: SPDXRef-map-22 229 | FileType: SOURCE 230 | FileChecksum: SHA1: a93e08f07d2ed77f28ad0970204425387847a0e3 231 | LicenseConcluded: NOASSERTION 232 | LicenseInfoInFile: NOASSERTION 233 | FileCopyrightText: NOASSERTION 234 | 235 | 236 | FileName: lib/src/value/simple_value.dart 237 | SPDXID: SPDXRef-simple-value-23 238 | FileType: SOURCE 239 | FileChecksum: SHA1: 0e50b1051660068a7c429b8947c3453dc757d204 240 | LicenseConcluded: NOASSERTION 241 | LicenseInfoInFile: NOASSERTION 242 | FileCopyrightText: NOASSERTION 243 | 244 | 245 | FileName: lib/src/value/string.dart 246 | SPDXID: SPDXRef-string-24 247 | FileType: SOURCE 248 | FileChecksum: SHA1: c8b770a04adefaa569c88be32ae0df184797494d 249 | LicenseConcluded: NOASSERTION 250 | LicenseInfoInFile: NOASSERTION 251 | FileCopyrightText: NOASSERTION 252 | 253 | 254 | FileName: lib/src/value/value.dart 255 | SPDXID: SPDXRef-value-25 256 | FileType: SOURCE 257 | FileChecksum: SHA1: ef8e0453a79c3e6d031f90e80c4196efb8487360 258 | LicenseConcluded: NOASSERTION 259 | LicenseInfoInFile: NOASSERTION 260 | FileCopyrightText: NOASSERTION 261 | 262 | 263 | FileName: pubspec.yaml 264 | SPDXID: SPDXRef-pubspec-26 265 | FileType: TEXT 266 | FileChecksum: SHA1: 88860152ed01eb534004d6bc49c1ca0bcf92355f 267 | LicenseConcluded: NOASSERTION 268 | LicenseInfoInFile: NOASSERTION 269 | FileCopyrightText: NOASSERTION 270 | 271 | 272 | Relationship: SPDXRef-pubspec-26 DEPENDENCY_MANIFEST_OF SPDXRef-Package-cbor 273 | 274 | 275 | -------------------------------------------------------------------------------- /test/supplementary_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:typed_data'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:test/test.dart'; 12 | 13 | void main() { 14 | group('Known patterns', () { 15 | test('Pattern 1 -> ', () { 16 | final encoded = cbor.encode( 17 | CborMap({ 18 | CborString('p16'): CborSmallInt(16), 19 | CborString('uni'): CborString( 20 | '\u901A\u8A0A\u9023\u63A5\u57E0 (COM1)', 21 | ), 22 | CborString('n1'): CborSmallInt(-1), 23 | CborString('ascii'): CborString('hello'), 24 | CborString('nil'): CborNull(), 25 | CborString('empty_arr'): CborList([]), 26 | CborString('p65535'): CborSmallInt(65535), 27 | CborString('bin'): CborBytes([0x31, 0x32, 0x55]), 28 | CborString('n2G'): CborSmallInt(-2147483648), 29 | CborString('p1'): CborSmallInt(1), 30 | CborString('n65535'): CborSmallInt(-65535), 31 | CborString('n16'): CborSmallInt(-16), 32 | CborString('zero'): CborSmallInt(0), 33 | CborString('arr'): CborList([ 34 | CborSmallInt(1), 35 | CborSmallInt(2), 36 | CborSmallInt(3), 37 | ]), 38 | CborString('obj'): CborMap({CborString('foo'): CborString('bar')}), 39 | CborString('bfalse'): CborBool(false), 40 | CborString('p255'): CborSmallInt(255), 41 | CborString('p2G'): CborSmallInt(2147483648), 42 | CborString('n255'): CborSmallInt(-255), 43 | CborString('btrue'): CborBool(true), 44 | }), 45 | ); 46 | 47 | expect(encoded, [ 48 | 0xB4, 49 | 0x63, 50 | 0x70, 51 | 0x31, 52 | 0x36, 53 | 0x10, 54 | 0x63, 55 | 0x75, 56 | 0x6E, 57 | 0x69, 58 | 0x76, 59 | 0xE9, 60 | 0x80, 61 | 0x9A, 62 | 0xE8, 63 | 0xA8, 64 | 0x8A, 65 | 0xE9, 66 | 0x80, 67 | 0xA3, 68 | 0xE6, 69 | 0x8E, 70 | 0xA5, 71 | 0xE5, 72 | 0x9F, 73 | 0xA0, 74 | 0x20, 75 | 0x28, 76 | 0x43, 77 | 0x4F, 78 | 0x4D, 79 | 0x31, 80 | 0x29, 81 | 0x62, 82 | 0x6E, 83 | 0x31, 84 | 0x20, 85 | 0x65, 86 | 0x61, 87 | 0x73, 88 | 0x63, 89 | 0x69, 90 | 0x69, 91 | 0x65, 92 | 0x68, 93 | 0x65, 94 | 0x6C, 95 | 0x6C, 96 | 0x6F, 97 | 0x63, 98 | 0x6E, 99 | 0x69, 100 | 0x6C, 101 | 0xF6, 102 | 0x69, 103 | 0x65, 104 | 0x6D, 105 | 0x70, 106 | 0x74, 107 | 0x79, 108 | 0x5F, 109 | 0x61, 110 | 0x72, 111 | 0x72, 112 | 0x80, 113 | 0x66, 114 | 0x70, 115 | 0x36, 116 | 0x35, 117 | 0x35, 118 | 0x33, 119 | 0x35, 120 | 0x19, 121 | 0xFF, 122 | 0xFF, 123 | 0x63, 124 | 0x62, 125 | 0x69, 126 | 0x6E, 127 | 0x43, 128 | 0x31, 129 | 0x32, 130 | 0x55, 131 | 0x63, 132 | 0x6E, 133 | 0x32, 134 | 0x47, 135 | 0x3A, 136 | 0x7F, 137 | 0xFF, 138 | 0xFF, 139 | 0xFF, 140 | 0x62, 141 | 0x70, 142 | 0x31, 143 | 0x01, 144 | 0x66, 145 | 0x6E, 146 | 0x36, 147 | 0x35, 148 | 0x35, 149 | 0x33, 150 | 0x35, 151 | 0x39, 152 | 0xFF, 153 | 0xFE, 154 | 0x63, 155 | 0x6E, 156 | 0x31, 157 | 0x36, 158 | 0x2F, 159 | 0x64, 160 | 0x7A, 161 | 0x65, 162 | 0x72, 163 | 0x6F, 164 | 0x00, 165 | 0x63, 166 | 0x61, 167 | 0x72, 168 | 0x72, 169 | 0x83, 170 | 0x01, 171 | 0x02, 172 | 0x03, 173 | 0x63, 174 | 0x6F, 175 | 0x62, 176 | 0x6A, 177 | 0xA1, 178 | 0x63, 179 | 0x66, 180 | 0x6F, 181 | 0x6F, 182 | 0x63, 183 | 0x62, 184 | 0x61, 185 | 0x72, 186 | 0x66, 187 | 0x62, 188 | 0x66, 189 | 0x61, 190 | 0x6C, 191 | 0x73, 192 | 0x65, 193 | 0xF4, 194 | 0x64, 195 | 0x70, 196 | 0x32, 197 | 0x35, 198 | 0x35, 199 | 0x18, 200 | 0xFF, 201 | 0x63, 202 | 0x70, 203 | 0x32, 204 | 0x47, 205 | 0x1A, 206 | 0x80, 207 | 0x00, 208 | 0x00, 209 | 0x00, 210 | 0x64, 211 | 0x6E, 212 | 0x32, 213 | 0x35, 214 | 0x35, 215 | 0x38, 216 | 0xFE, 217 | 0x65, 218 | 0x62, 219 | 0x74, 220 | 0x72, 221 | 0x75, 222 | 0x65, 223 | 0xF5, 224 | ]); 225 | }); 226 | }); 227 | 228 | group('Error handling', () { 229 | test('No input', () { 230 | expect(() => cbor.decode([]), throwsException); 231 | }); 232 | 233 | test('Random bytes', () { 234 | expect(() => cbor.decode([0xcd, 0xfe, 0x00]), throwsException); 235 | }); 236 | 237 | test('Premature termination', () { 238 | expect(() => cbor.decode([0x44, 0x01, 0x02, 0x03]), throwsException); 239 | }); 240 | 241 | test('Cyclic reference', () { 242 | final list = CborList([]); 243 | list.add(list); 244 | 245 | expect(() => cbor.encode(list), throwsA(isA())); 246 | }); 247 | }); 248 | 249 | group('Bignum', () { 250 | test('Big Num Positive', () { 251 | final test = BigInt.from(922337203685477580767.0); 252 | expect(cbor.decode(cbor.encode(CborBigInt(test))).toObject(), test); 253 | }); 254 | 255 | test('Big Num Negative', () { 256 | final test = BigInt.from(-922337203685477580767.0); 257 | expect(cbor.decode(cbor.encode(CborBigInt(test))).toObject(), test); 258 | }); 259 | }); 260 | 261 | group('constructor', () { 262 | test('Test 1', () { 263 | expect( 264 | CborValue([ 265 | {'a': 'b'}, 266 | null, 267 | [1, 2, 3], 268 | Uint8List.fromList([1, 2, 3]), 269 | DateTime.fromMillisecondsSinceEpoch(2000), 270 | ], dateTimeEpoch: true), 271 | CborList([ 272 | CborMap({CborString('a'): CborString('b')}), 273 | CborNull(), 274 | CborList([CborSmallInt(1), CborSmallInt(2), CborSmallInt(3)]), 275 | CborBytes([1, 2, 3]), 276 | CborDateTimeInt.fromSecondsSinceEpoch(2), 277 | ]), 278 | ); 279 | }); 280 | }); 281 | 282 | group('toObject', () { 283 | test('Test 1', () { 284 | expect( 285 | CborValue([ 286 | {'a': 'b'}, 287 | null, 288 | [1, 2, 3], 289 | Uint8List.fromList([1, 2, 3]), 290 | DateTime.fromMillisecondsSinceEpoch(2000), 291 | ], dateTimeEpoch: true).toObject(), 292 | [ 293 | {'a': 'b'}, 294 | null, 295 | [1, 2, 3], 296 | [1, 2, 3], 297 | DateTime.fromMillisecondsSinceEpoch(2000).toUtc(), 298 | ], 299 | ); 300 | }); 301 | }); 302 | 303 | group('Floating point precision', () { 304 | test('Automatic', () { 305 | final encoded = cbor.encode(CborFloat(0.0)); 306 | expect(encoded, [0xf9, 0x00, 0x00]); 307 | }); 308 | test('Half', () { 309 | final encoded = cbor.encode(CborFloat(0.0)..halfPrecision()); 310 | expect(encoded, [0xf9, 0x00, 0x00]); 311 | }); 312 | test('Float', () { 313 | final encoded = cbor.encode(CborFloat(100000.0)..floatPrecision()); 314 | expect(encoded, [0xfa, 0x47, 0xc3, 0x50, 0x00]); 315 | }); 316 | test('Double', () { 317 | final encoded = cbor.encode(CborFloat(1.0e+300)..doublePrecision()); 318 | expect(encoded, [0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c]); 319 | }); 320 | test('Half - value to large', () { 321 | bool raised = false; 322 | try { 323 | cbor.encode(CborFloat(100000.0)..halfPrecision()); 324 | } on ArgumentError { 325 | raised = true; 326 | } 327 | expect(raised, true); 328 | }); 329 | test('Float - value to large', () { 330 | bool raised = false; 331 | try { 332 | cbor.encode(CborFloat(1.0e+300)..floatPrecision()); 333 | } on ArgumentError { 334 | raised = true; 335 | } 336 | expect(raised, true); 337 | }); 338 | }); 339 | } 340 | -------------------------------------------------------------------------------- /lib/src/value/bytes.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | import 'dart:typed_data'; 10 | 11 | import 'package:cbor/cbor.dart'; 12 | import 'package:collection/collection.dart'; 13 | import 'package:hex/hex.dart'; 14 | 15 | import '../constants.dart'; 16 | import '../encoder/sink.dart'; 17 | import '../utils/arg.dart'; 18 | import 'internal.dart'; 19 | 20 | /// A CBOR byte array. 21 | abstract class CborBytes extends CborValue { 22 | List get bytes; 23 | 24 | List> get bytesList; 25 | 26 | CborLengthType get type; 27 | 28 | factory CborBytes(List bytes, {List tags}) = _CborBytesImpl; 29 | 30 | factory CborBytes.indefinite(List> bytes, {List tags}) = 31 | _CborBytesIndefiniteLengthImpl; 32 | } 33 | 34 | class _CborBytesImpl with CborValueMixin implements CborBytes { 35 | @override 36 | final List bytes; 37 | @override 38 | final List tags; 39 | @override 40 | final CborLengthType type = CborLengthType.definite; 41 | 42 | @override 43 | List> get bytesList => [bytes]; 44 | 45 | @override 46 | int get hashCode => Object.hashAll([bytes, tags].flattened); 47 | 48 | const _CborBytesImpl(this.bytes, {this.tags = const []}); 49 | 50 | @override 51 | String toString() => bytes.toString(); 52 | 53 | @override 54 | bool operator ==(Object other) => 55 | other is CborBytes && 56 | tags.equals(other.tags) && 57 | bytes.equals(other.bytes); 58 | 59 | @override 60 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 61 | return bytes; 62 | } 63 | 64 | @override 65 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 66 | switch (expectedConversion ?? o.encoding) { 67 | case JsonBytesEncoding.base16: 68 | return const HexEncoder(upperCase: true).convert(bytes); 69 | case JsonBytesEncoding.base64: 70 | return base64.encode(bytes); 71 | case JsonBytesEncoding.base64Url: 72 | return base64Url.encode(bytes).replaceAll('=', ''); 73 | } 74 | } 75 | 76 | @override 77 | void encode(EncodeSink sink) { 78 | CborEncodeDefiniteLengthBytes(this).encode(sink); 79 | } 80 | } 81 | 82 | class _CborBytesIndefiniteLengthImpl with CborValueMixin implements CborBytes { 83 | @override 84 | final List tags; 85 | @override 86 | final CborLengthType type = CborLengthType.indefinite; 87 | @override 88 | final List> bytesList; 89 | 90 | @override 91 | List get bytes => bytesList.flattened.toList(growable: false); 92 | 93 | @override 94 | int get hashCode => Object.hashAll([bytesList, tags].flattened); 95 | 96 | const _CborBytesIndefiniteLengthImpl(this.bytesList, {this.tags = const []}); 97 | 98 | @override 99 | String toString() => bytes.toString(); 100 | 101 | @override 102 | bool operator ==(Object other) => 103 | other is _CborBytesIndefiniteLengthImpl && 104 | tags.equals(other.tags) && 105 | DeepCollectionEquality().equals(bytesList, other.bytesList); 106 | 107 | @override 108 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 109 | return bytes; 110 | } 111 | 112 | @override 113 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 114 | switch (expectedConversion ?? o.encoding) { 115 | case JsonBytesEncoding.base16: 116 | return const HexEncoder(upperCase: true).convert(bytes); 117 | case JsonBytesEncoding.base64: 118 | return base64.encode(bytes); 119 | case JsonBytesEncoding.base64Url: 120 | return base64Url.encode(bytes).replaceAll('=', ''); 121 | } 122 | } 123 | 124 | @override 125 | void encode(EncodeSink sink) { 126 | CborEncodeIndefiniteLengthBytes(bytesList).encode(sink); 127 | } 128 | } 129 | 130 | /// Use this to force the [CborEncoder] to encode an indefinite length byte string. 131 | /// 132 | /// This is never generated by decoder. 133 | abstract class CborEncodeIndefiniteLengthBytes extends CborValue { 134 | factory CborEncodeIndefiniteLengthBytes( 135 | List> items, { 136 | List tags, 137 | }) = _CborEncodeIndefiniteLengthBytesImpl; 138 | } 139 | 140 | class _CborEncodeIndefiniteLengthBytesImpl 141 | with CborValueMixin 142 | implements CborEncodeIndefiniteLengthBytes { 143 | final List> items; 144 | @override 145 | final List tags; 146 | 147 | const _CborEncodeIndefiniteLengthBytesImpl( 148 | this.items, { 149 | this.tags = const [], 150 | }); 151 | 152 | @override 153 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 154 | return CborBytes( 155 | items.flattened.toList(), 156 | tags: tags, 157 | ).toObjectInternal(cyclicCheck, o); 158 | } 159 | 160 | @override 161 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 162 | return CborBytes( 163 | items.flattened.toList(), 164 | tags: tags, 165 | ).toJsonInternal(cyclicCheck, o); 166 | } 167 | 168 | @override 169 | void encode(EncodeSink sink) { 170 | sink.addTags(tags); 171 | 172 | sink.addHeaderInfo(CborConstants.two, Arg.indefiniteLength); 173 | 174 | for (final value in items) { 175 | CborEncodeDefiniteLengthBytes(CborBytes(value)).encode(sink); 176 | } 177 | 178 | (const Break()).encode(sink); 179 | } 180 | } 181 | 182 | /// Use this to force the [CborEncoder] to encode an definite length byte string. 183 | /// 184 | /// This is never generated by decoder. 185 | abstract class CborEncodeDefiniteLengthBytes extends CborValue { 186 | factory CborEncodeDefiniteLengthBytes(CborBytes input) = 187 | _CborEncodeDefiniteLengthBytesImpl; 188 | } 189 | 190 | class _CborEncodeDefiniteLengthBytesImpl 191 | with CborValueMixin 192 | implements CborEncodeDefiniteLengthBytes { 193 | final CborBytes inner; 194 | 195 | @override 196 | List get tags => inner.tags; 197 | 198 | const _CborEncodeDefiniteLengthBytesImpl(this.inner); 199 | 200 | @override 201 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 202 | return inner.toObjectInternal(cyclicCheck, o); 203 | } 204 | 205 | @override 206 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 207 | return inner.toJsonInternal(cyclicCheck, o); 208 | } 209 | 210 | @override 211 | void encode(EncodeSink sink) { 212 | sink.addTags(tags); 213 | 214 | sink.addHeaderInfo(CborConstants.two, Arg.int(inner.bytes.length)); 215 | 216 | sink.add(inner.bytes); 217 | } 218 | } 219 | 220 | /// A CBOR big int. 221 | abstract class CborBigInt extends CborBytes implements CborInt { 222 | bool get isNegative; 223 | 224 | factory CborBigInt(BigInt value, [List? tags]) { 225 | final negative = value.isNegative; 226 | if (value.isNegative) { 227 | tags ??= [CborTag.negativeBignum]; 228 | value = ~value; 229 | } else { 230 | tags ??= [CborTag.positiveBignum]; 231 | } 232 | 233 | final b = Uint8List( 234 | (value.bitLength + CborConstants.seven) ~/ CborConstants.byteLength, 235 | ); 236 | 237 | for (var i = b.length - 1; i >= 0; i--) { 238 | b[i] = value.toUnsigned(CborConstants.byteLength).toInt(); 239 | value >>= CborConstants.byteLength; 240 | } 241 | 242 | return negative 243 | ? CborBigInt.fromNegativeBytes(b, tags: tags) 244 | : CborBigInt.fromBytes(b, tags: tags); 245 | } 246 | 247 | factory CborBigInt.fromBytes(List bytes, {List tags}) = 248 | _CborBigIntImpl.fromBytes; 249 | 250 | factory CborBigInt.fromNegativeBytes(List bytes, {List tags}) = 251 | _CborBigIntImpl.fromNegativeBytes; 252 | } 253 | 254 | class _CborBigIntImpl extends _CborBytesImpl implements CborBigInt { 255 | @override 256 | final bool isNegative; 257 | 258 | const _CborBigIntImpl.fromBytes( 259 | super.bytes, { 260 | super.tags = const [CborTag.positiveBignum], 261 | }) : isNegative = false; 262 | 263 | const _CborBigIntImpl.fromNegativeBytes( 264 | super.bytes, { 265 | super.tags = const [CborTag.negativeBignum], 266 | }) : isNegative = true; 267 | 268 | @override 269 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 270 | return toBigInt(); 271 | } 272 | 273 | @override 274 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 275 | final output = StringBuffer(); 276 | 277 | if (isNegative) { 278 | output.write('~'); 279 | } 280 | 281 | output.write( 282 | super.toJsonInternal( 283 | cyclicCheck, 284 | o.copyWith(encoding: JsonBytesEncoding.base64Url), 285 | ), 286 | ); 287 | return output.toString(); 288 | } 289 | 290 | @override 291 | BigInt toBigInt() { 292 | var data = BigInt.zero; 293 | for (final b in bytes) { 294 | data <<= CborConstants.byteLength; 295 | data |= BigInt.from(b); 296 | } 297 | 298 | return isNegative ? ~data : data; 299 | } 300 | 301 | @override 302 | int toInt() => toBigInt().toInt(); 303 | } 304 | -------------------------------------------------------------------------------- /lib/src/value/value.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | // dart format width=123 9 | 10 | import 'dart:typed_data'; 11 | 12 | import 'package:cbor/cbor.dart'; 13 | import 'package:meta/meta.dart'; 14 | 15 | import '../constants.dart'; 16 | import '../encoder/sink.dart'; 17 | import 'internal.dart'; 18 | 19 | export 'bytes.dart'; 20 | export 'double.dart'; 21 | export 'int.dart'; 22 | export 'list.dart'; 23 | export 'map.dart'; 24 | export 'simple_value.dart'; 25 | export 'string.dart'; 26 | 27 | /// Jump table for initial byte values can be found here. 28 | /// Maybe useful for code maintainers. 29 | 30 | /// Major types 31 | sealed class CborMajorType { 32 | static const int uint = 0; // unsigned integer N 33 | static const int nint = 1; // negative integer -1-N 34 | static const int byteString = 2; // byte string 35 | static const int textString = 3; // text string 36 | static const int array = 4; // array 37 | static const int map = 5; // map 38 | static const int tag = 6; // tag of number N 39 | static const int simpleFloat = 7; // simple/float 40 | 41 | CborMajorType._(); 42 | } 43 | 44 | /// Additional Info 45 | sealed class CborAdditionalInfo { 46 | static const simpleValueLow = 23; // Simple value (value 0..23) 47 | static const simpleValueHigh = 24; // Simple value (value 32..255 in following byte) 48 | static const halfPrecisionFloat = 25; // IEEE 754 Half-Precision Float (16 bits follow) 49 | static const singlePrecisionFloat = 26; // IEEE 754 Single-Precision Float (32 bits follow) 50 | static const doublePrecisionFloat = 27; // IEEE 754 Double-Precision Float (64 bits follow) 51 | static const breakStop = 31; // Break" stop code for indefinite-length items 52 | static const simpleFalse = 20; 53 | static const simpleTrue = 21; 54 | static const simpleNull = 22; 55 | static const simpleUndefined = 23; 56 | } 57 | 58 | /// Hint for the content of something. 59 | sealed class CborTag { 60 | static const int dateTimeString = 0; 61 | static const int epochDateTime = 1; 62 | static const int positiveBignum = 2; 63 | static const int negativeBignum = 3; 64 | static const int decimalFraction = 4; 65 | static const int bigFloat = 5; 66 | static const int encodedCborData = 24; 67 | static const int expectedConversionToBase64 = 22; 68 | static const int expectedConversionToBase64Url = 21; 69 | static const int expectedConversionToBase16 = 23; 70 | static const int rationalNumber = 30; 71 | static const int uri = 32; 72 | static const int base64Url = 33; 73 | static const int base64 = 34; 74 | static const int regex = 35; 75 | static const int mime = 36; 76 | static const int selfDescribeCbor = 55799; 77 | 78 | CborTag._(); 79 | } 80 | 81 | const kCborDefiniteLengthThreshold = 256; 82 | 83 | enum CborLengthType { definite, indefinite, auto } 84 | 85 | /// A CBOR value. 86 | abstract class CborValue { 87 | /// Additional tags provided to the value. 88 | List get tags; 89 | 90 | factory CborValue._fromObject( 91 | Object? object, { 92 | required bool dateTimeEpoch, 93 | required Object? Function(dynamic object) toEncodable, 94 | Set? cycleCheck, 95 | }) { 96 | if (object == null) { 97 | return CborNull(); 98 | } else if (object is CborValue) { 99 | return object; 100 | } else if (object is int) { 101 | return CborSmallInt(object); 102 | } else if (object is BigInt) { 103 | return CborInt(object); 104 | } else if (object is double) { 105 | return CborFloat(object); 106 | } else if (object is bool) { 107 | return CborBool(object); 108 | } else if (object is Uint8List) { 109 | return CborBytes(object); 110 | } else if (object is String) { 111 | return CborString(object); 112 | } else if (object is DateTime) { 113 | if (!dateTimeEpoch) { 114 | return CborDateTimeString(object); 115 | } else if (object.millisecondsSinceEpoch % CborConstants.milliseconds == 0) { 116 | return CborDateTimeInt.fromSecondsSinceEpoch(object.millisecondsSinceEpoch ~/ CborConstants.milliseconds); 117 | } else { 118 | return CborDateTimeFloat.fromSecondsSinceEpoch(object.millisecondsSinceEpoch / CborConstants.milliseconds); 119 | } 120 | } else if (object is Uri) { 121 | return CborUri(object); 122 | } else if (object is Iterable) { 123 | cycleCheck ??= {}; 124 | 125 | if (!cycleCheck.add(object)) { 126 | throw CborCyclicError(object); 127 | } 128 | 129 | final value = CborList.of( 130 | object.map( 131 | (v) => CborValue._fromObject(v, dateTimeEpoch: dateTimeEpoch, toEncodable: toEncodable, cycleCheck: cycleCheck), 132 | ), 133 | ); 134 | 135 | cycleCheck.remove(object); 136 | 137 | return value; 138 | } else if (object is Map) { 139 | cycleCheck ??= {}; 140 | 141 | if (!cycleCheck.add(object)) { 142 | throw CborCyclicError(object); 143 | } 144 | 145 | final value = CborMap.fromEntries( 146 | object.entries.map( 147 | (entry) => MapEntry( 148 | CborValue._fromObject( 149 | entry.key, 150 | dateTimeEpoch: dateTimeEpoch, 151 | toEncodable: toEncodable, 152 | cycleCheck: cycleCheck, 153 | ), 154 | CborValue._fromObject( 155 | entry.value, 156 | dateTimeEpoch: dateTimeEpoch, 157 | toEncodable: toEncodable, 158 | cycleCheck: cycleCheck, 159 | ), 160 | ), 161 | ), 162 | ); 163 | 164 | cycleCheck.remove(object); 165 | 166 | return value; 167 | } else { 168 | return CborValue._fromObject( 169 | toEncodable(object), 170 | dateTimeEpoch: dateTimeEpoch, 171 | toEncodable: toEncodable, 172 | cycleCheck: cycleCheck, 173 | ); 174 | } 175 | } 176 | 177 | /// Transform the [Object] into a CborValue. 178 | /// 179 | /// Throws [CborCyclicError] if cyclic references exist inside [object]. 180 | /// 181 | /// The object is transformed according to the following relations. 182 | /// 183 | /// | Input | Output | 184 | /// |---------------------------|-----------------------------| 185 | /// | `null` | [CborNull] | 186 | /// | [CborValue] | [CborValue] (unchanged) | 187 | /// | [double] | [CborFloat] | 188 | /// | [int] | [CborSmallInt] | 189 | /// | [BigInt] | [CborInt] | 190 | /// | [bool] | [CborBool] | 191 | /// | [Uint8List] | [CborBytes] | 192 | /// | [String] | [CborString] | 193 | /// | [DateTime] | [CborDateTime] | 194 | /// | [Uri] | [CborUri] | 195 | /// | [Iterable] | [CborList] | 196 | /// | [Map] | [CborMap] | 197 | /// 198 | /// If [dateTimeEpoch] is `false`, [DateTime] is transformed into 199 | /// [CborDateTimeString], and otherwise into [CborDateTimeInt] or 200 | /// [CborDateTimeFloat] depending on the sub-second resolution. 201 | /// 202 | /// The [toEncodable] function is used during encoding. It is invoked for 203 | /// values that are not directly encodable to a [CborValue]. The 204 | /// function must return an object that is directly encodable. The elements of 205 | /// a returned list and values of a returned map do not need to be directly 206 | /// encodable, and if they aren't, `toEncodable` will be used on them as well. 207 | /// Please notice that it is possible to cause an infinite recursive regress 208 | /// in this way, by effectively creating an infinite data structure through 209 | /// repeated call to `toEncodable`. 210 | /// 211 | /// If [toEncodable] is omitted, it defaults to a function that returns the 212 | /// result of calling `.toCbor()` on the unencodable object. 213 | factory CborValue(Object? object, {bool dateTimeEpoch = false, Object? Function(dynamic object)? toEncodable}) => 214 | CborValue._fromObject(object, dateTimeEpoch: dateTimeEpoch, toEncodable: toEncodable ?? (object) => object.toCbor()); 215 | 216 | /// Transforms the CborValue into a Dart object. 217 | /// 218 | /// Throws [CborCyclicError] if cyclic references exist inside `this`. 219 | /// 220 | /// Parsing may throw [FormatException]. 221 | /// 222 | /// The object is transformed according to the following relations. 223 | /// 224 | /// | Input | Output | 225 | /// |---------------------------|-----------------------------| 226 | /// | [CborSimpleValue] | [int] | 227 | /// | [CborNull] | `null` | 228 | /// | [CborUndefined] | `null` | 229 | /// | [CborBool] | [bool] | 230 | /// | [CborFloat] | [double] | 231 | /// | [CborDateTimeFloat] | if ([parseDateTime]) [DateTime] else [double] | 232 | /// | [CborInt] | [int] or [BigInt] | 233 | /// | [CborDateTimeInt] | if ([parseDateTime]) [DateTime] else [int] | 234 | /// | [CborBytes] | [List] | 235 | /// | [CborBigInt] | [BigInt] | 236 | /// | [CborString] | [String] | 237 | /// | [CborDateTimeString] | if ([parseDateTime]) [DateTime] else [String] | 238 | /// | [CborUri] | if ([parseUri]) [Uri] else [String] | 239 | /// | [CborBase64] | if ([decodeBase64]) [List] else [String] | 240 | /// | [CborBase64Url] | if ([decodeBase64]) [List] else [String] | 241 | /// | [CborRegex] | [String] | 242 | /// | [CborMime] | [String] | 243 | /// | [CborList] | [List] | 244 | /// | [CborDecimalFraction] | `[int exponent, BigInt mantissa]` | 245 | /// | [CborBigFloat] | `[int exponent, BigInt mantissa]` | 246 | /// | [CborMap] | [Map] | 247 | Object? toObject({ 248 | bool parseDateTime = true, 249 | bool parseUri = true, 250 | bool decodeBase64 = false, 251 | bool allowMalformedUtf8 = false, 252 | }); 253 | 254 | /// Transform this into a JSON encodable value. 255 | /// 256 | /// [substituteValue] will be used for values that cannot be encoded, such 257 | /// as [double.infinity], [double.nan], [CborUndefined]. 258 | /// 259 | /// If the keys for a map are not strings, they are encoded recursively 260 | /// as JSON, and the string is used. 261 | Object? toJson({Object? substituteValue, bool allowMalformedUtf8 = false}); 262 | 263 | @internal 264 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o); 265 | 266 | @internal 267 | void encode(EncodeSink sink); 268 | 269 | @internal 270 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o); 271 | } 272 | 273 | /// A CBOR datetime. 274 | abstract class CborDateTime implements CborValue { 275 | /// Converts the value to [DateTime], throwing [FormatException] if fails. 276 | DateTime toDateTime(); 277 | } 278 | -------------------------------------------------------------------------------- /lib/src/value/list.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'package:cbor/cbor.dart'; 9 | import 'package:collection/collection.dart'; 10 | 11 | import '../encoder/sink.dart'; 12 | import '../utils/arg.dart'; 13 | import 'internal.dart'; 14 | 15 | /// A CBOR array. 16 | abstract class CborList extends CborValue implements List { 17 | CborLengthType get type; 18 | 19 | /// Create a new [CborList] from a view of the given list. 20 | factory CborList( 21 | List items, { 22 | List tags, 23 | CborLengthType type, 24 | }) = _CborListImpl; 25 | 26 | /// Create a new [CborList] from values. 27 | /// 28 | /// The resulting list is growable. 29 | factory CborList.of( 30 | Iterable elements, { 31 | List tags, 32 | CborLengthType type, 33 | }) = _CborListImpl.of; 34 | 35 | /// Create a new [CborList] from generator. 36 | /// 37 | /// The resulting list is growable. 38 | factory CborList.generate( 39 | int len, 40 | CborValue Function(int index) f, { 41 | List tags, 42 | }) = _CborListImpl.generate; 43 | } 44 | 45 | class _CborListImpl extends DelegatingList 46 | with CborValueMixin 47 | implements CborList { 48 | @override 49 | final List tags; 50 | 51 | @override 52 | final CborLengthType type; 53 | 54 | const _CborListImpl( 55 | super.items, { 56 | this.tags = const [], 57 | this.type = CborLengthType.auto, 58 | }); 59 | 60 | _CborListImpl.of( 61 | Iterable elements, { 62 | this.tags = const [], 63 | this.type = CborLengthType.auto, 64 | }) : super(List.of(elements)); 65 | 66 | _CborListImpl.generate( 67 | int len, 68 | CborValue Function(int index) f, { 69 | this.tags = const [], 70 | this.type = CborLengthType.auto, 71 | }) : super(List.generate(len, f)); 72 | 73 | @override 74 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 75 | if (!cyclicCheck.add(this)) { 76 | throw CborCyclicError(this); 77 | } 78 | 79 | final res = map((i) => i.toObjectInternal(cyclicCheck, o)).toList(); 80 | 81 | cyclicCheck.remove(this); 82 | 83 | return res; 84 | } 85 | 86 | @override 87 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 88 | if (!cyclicCheck.add(this)) { 89 | throw CborCyclicError(this); 90 | } 91 | 92 | final res = 93 | map( 94 | (i) => i.toJsonInternal( 95 | cyclicCheck, 96 | o.copyWith(encoding: expectedConversion), 97 | ), 98 | ).toList(); 99 | 100 | cyclicCheck.remove(this); 101 | 102 | return res; 103 | } 104 | 105 | @override 106 | void encode(EncodeSink sink) { 107 | if (type == CborLengthType.definite || 108 | (type == CborLengthType.auto && 109 | length < kCborDefiniteLengthThreshold)) { 110 | CborEncodeDefiniteLengthList(this).encode(sink); 111 | } else { 112 | // Indefinite length 113 | CborEncodeIndefiniteLengthList(this).encode(sink); 114 | } 115 | } 116 | } 117 | 118 | /// Use this to force the [CborEncoder] to encode an indefinite length list. 119 | /// 120 | /// This is never generated by decoder. 121 | abstract class CborEncodeIndefiniteLengthList extends CborValue { 122 | factory CborEncodeIndefiniteLengthList(CborList x) = 123 | _CborEncodeIndefiniteLengthListImpl; 124 | } 125 | 126 | class _CborEncodeIndefiniteLengthListImpl 127 | with CborValueMixin 128 | implements CborEncodeIndefiniteLengthList { 129 | final CborList inner; 130 | 131 | @override 132 | List get tags => inner.tags; 133 | 134 | const _CborEncodeIndefiniteLengthListImpl(this.inner); 135 | 136 | @override 137 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 138 | return inner.toJsonInternal(cyclicCheck, o); 139 | } 140 | 141 | @override 142 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 143 | return inner.toObjectInternal(cyclicCheck, o); 144 | } 145 | 146 | @override 147 | void encode(EncodeSink sink) { 148 | sink.addTags(tags); 149 | 150 | sink.addHeaderInfo(CborMajorType.array, Arg.indefiniteLength); 151 | 152 | sink.addToCycleCheck(inner); 153 | for (final x in inner) { 154 | x.encode(sink); 155 | } 156 | 157 | sink.removeFromCycleCheck(inner); 158 | 159 | (const Break()).encode(sink); 160 | } 161 | } 162 | 163 | /// Use this to force the [CborEncoder] to encode an definite length list. 164 | /// 165 | /// This is never generated by decoder. 166 | abstract class CborEncodeDefiniteLengthList extends CborValue { 167 | factory CborEncodeDefiniteLengthList(CborList list) = 168 | _CborEncodeDefiniteLengthListImpl; 169 | } 170 | 171 | class _CborEncodeDefiniteLengthListImpl 172 | with CborValueMixin 173 | implements CborEncodeDefiniteLengthList { 174 | final CborList inner; 175 | 176 | @override 177 | List get tags => inner.tags; 178 | 179 | const _CborEncodeDefiniteLengthListImpl(this.inner); 180 | 181 | @override 182 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 183 | return inner.toObjectInternal(cyclicCheck, o); 184 | } 185 | 186 | @override 187 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 188 | return inner.toJsonInternal(cyclicCheck, o); 189 | } 190 | 191 | @override 192 | void encode(EncodeSink sink) { 193 | sink.addTags(tags); 194 | 195 | sink.addHeaderInfo(CborMajorType.array, Arg.int(inner.length)); 196 | 197 | sink.addToCycleCheck(inner); 198 | for (final x in inner) { 199 | x.encode(sink); 200 | } 201 | sink.removeFromCycleCheck(inner); 202 | } 203 | } 204 | 205 | /// A CBOR fraction (m * (10 ** e)). 206 | abstract class CborDecimalFraction extends CborList { 207 | CborInt get exponent; 208 | 209 | CborInt get mantissa; 210 | 211 | factory CborDecimalFraction({ 212 | required CborInt exponent, 213 | required CborInt mantissa, 214 | List tags, 215 | CborLengthType type, 216 | }) = _CborDecimalFractionImpl; 217 | } 218 | 219 | class _CborDecimalFractionImpl extends DelegatingList 220 | with CborValueMixin 221 | implements CborDecimalFraction { 222 | @override 223 | final CborInt exponent; 224 | @override 225 | final CborInt mantissa; 226 | 227 | @override 228 | final List tags; 229 | 230 | @override 231 | final CborLengthType type; 232 | 233 | _CborDecimalFractionImpl({ 234 | required this.exponent, 235 | required this.mantissa, 236 | this.tags = const [CborTag.decimalFraction], 237 | this.type = CborLengthType.auto, 238 | }) : super(List.of([exponent, mantissa], growable: false)); 239 | 240 | @override 241 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 242 | return [exponent.toInt(), mantissa.toBigInt()]; 243 | } 244 | 245 | @override 246 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 247 | return [ 248 | exponent.toJsonInternal(cyclicCheck, o), 249 | mantissa.toJsonInternal(cyclicCheck, o), 250 | ]; 251 | } 252 | 253 | @override 254 | void encode(EncodeSink sink) { 255 | sink.addTags(tags); 256 | sink.addHeaderInfo(CborMajorType.array, switch (type) { 257 | CborLengthType.definite || CborLengthType.auto => const Arg.int(2), 258 | CborLengthType.indefinite => Arg.indefiniteLength, 259 | }); 260 | exponent.encode(sink); 261 | mantissa.encode(sink); 262 | 263 | if (type == CborLengthType.indefinite) { 264 | (const Break()).encode(sink); 265 | } 266 | } 267 | } 268 | 269 | /// A CBOR rational number (m / n). 270 | /// https://peteroupc.github.io/CBOR/rational.html 271 | abstract class CborRationalNumber extends CborList { 272 | CborInt get numerator; 273 | 274 | CborInt get denominator; 275 | 276 | factory CborRationalNumber({ 277 | required CborInt numerator, 278 | required CborInt denominator, 279 | List tags, 280 | CborLengthType type, 281 | }) = _CborRationalNumberImpl; 282 | } 283 | 284 | class _CborRationalNumberImpl extends DelegatingList 285 | with CborValueMixin 286 | implements CborRationalNumber { 287 | @override 288 | final CborInt numerator; 289 | @override 290 | final CborInt denominator; 291 | 292 | @override 293 | final List tags; 294 | 295 | @override 296 | final CborLengthType type; 297 | 298 | _CborRationalNumberImpl({ 299 | required this.numerator, 300 | required this.denominator, 301 | this.tags = const [CborTag.rationalNumber], 302 | this.type = CborLengthType.auto, 303 | }) : assert(denominator.toInt() != 0), 304 | super(List.of([numerator, denominator], growable: false)); 305 | 306 | @override 307 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 308 | return [numerator.toBigInt(), denominator.toBigInt()]; 309 | } 310 | 311 | @override 312 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 313 | return [ 314 | numerator.toJsonInternal(cyclicCheck, o), 315 | denominator.toJsonInternal(cyclicCheck, o), 316 | ]; 317 | } 318 | 319 | @override 320 | void encode(EncodeSink sink) { 321 | sink.addTags(tags); 322 | sink.addHeaderInfo(CborMajorType.array, switch (type) { 323 | CborLengthType.definite || CborLengthType.auto => const Arg.int(2), 324 | CborLengthType.indefinite => Arg.indefiniteLength, 325 | }); 326 | numerator.encode(sink); 327 | denominator.encode(sink); 328 | 329 | if (type == CborLengthType.indefinite) { 330 | (const Break()).encode(sink); 331 | } 332 | } 333 | } 334 | 335 | /// A CBOR fraction (m * (2 ** e)). 336 | abstract class CborBigFloat extends CborList { 337 | CborInt get exponent; 338 | 339 | CborInt get mantissa; 340 | 341 | factory CborBigFloat({ 342 | required CborInt exponent, 343 | required CborInt mantissa, 344 | List tags, 345 | CborLengthType type, 346 | }) = _CborBigFloatImpl; 347 | } 348 | 349 | class _CborBigFloatImpl extends DelegatingList 350 | with CborValueMixin 351 | implements CborBigFloat { 352 | @override 353 | final CborInt exponent; 354 | @override 355 | final CborInt mantissa; 356 | 357 | @override 358 | final List tags; 359 | @override 360 | final CborLengthType type; 361 | 362 | _CborBigFloatImpl({ 363 | required this.exponent, 364 | required this.mantissa, 365 | this.tags = const [CborTag.bigFloat], 366 | this.type = CborLengthType.auto, 367 | }) : super(List.of([exponent, mantissa], growable: false)); 368 | 369 | @override 370 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 371 | return [exponent.toInt(), mantissa.toBigInt()]; 372 | } 373 | 374 | @override 375 | void encode(EncodeSink sink) { 376 | sink.addTags(tags); 377 | sink.addHeaderInfo(CborMajorType.array, switch (type) { 378 | CborLengthType.definite || CborLengthType.auto => const Arg.int(2), 379 | CborLengthType.indefinite => Arg.indefiniteLength, 380 | }); 381 | exponent.encode(sink); 382 | mantissa.encode(sink); 383 | 384 | if (type == CborLengthType.indefinite) { 385 | (const Break()).encode(sink); 386 | } 387 | } 388 | 389 | @override 390 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 391 | return [ 392 | exponent.toJsonInternal(cyclicCheck, o), 393 | mantissa.toJsonInternal(cyclicCheck, o), 394 | ]; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /test/simple_encoder_rfc_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:typed_data'; 9 | 10 | import 'package:cbor/simple.dart'; 11 | import 'package:test/test.dart'; 12 | 13 | void main() { 14 | group('RFC Appendix A Diagnostics encoder tests -> ', () { 15 | test('0', () { 16 | final encoded = cbor.encode(0); 17 | expect(encoded, [0x00]); 18 | }); 19 | 20 | test('1', () { 21 | final encoded = cbor.encode(1); 22 | expect(encoded, [0x01]); 23 | }); 24 | 25 | test('10', () { 26 | final encoded = cbor.encode(10); 27 | expect(encoded, [0x0A]); 28 | }); 29 | 30 | test('23', () { 31 | final encoded = cbor.encode(23); 32 | expect(encoded, [0x17]); 33 | }); 34 | 35 | test('24', () { 36 | final encoded = cbor.encode(24); 37 | expect(encoded, [0x18, 0x18]); 38 | }); 39 | 40 | test('25', () { 41 | final encoded = cbor.encode(25); 42 | expect(encoded, [0x18, 0x19]); 43 | }); 44 | 45 | test('100', () { 46 | final encoded = cbor.encode(100); 47 | expect(encoded, [0x18, 0x64]); 48 | }); 49 | 50 | test('1000', () { 51 | final encoded = cbor.encode(1000); 52 | expect(encoded, [0x19, 0x03, 0xe8]); 53 | }); 54 | 55 | test('1000000', () { 56 | final encoded = cbor.encode(1000000); 57 | expect(encoded, [0x1a, 0x00, 0x0f, 0x42, 0x40]); 58 | }); 59 | 60 | test('1000000000000', () { 61 | final encoded = cbor.encode(1000000000000); 62 | expect(encoded, [0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00]); 63 | }); 64 | 65 | test('18446744073709551615', () { 66 | final encoded = cbor.encode(BigInt.parse('18446744073709551615')); 67 | expect(encoded, [0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); 68 | }); 69 | 70 | test('18446744073709551616', () { 71 | final encoded = cbor.encode(BigInt.parse('18446744073709551616')); 72 | expect(encoded, [ 73 | 0xc2, 74 | 0x49, 75 | 0x01, 76 | 0x00, 77 | 0x00, 78 | 0x00, 79 | 0x00, 80 | 0x00, 81 | 0x00, 82 | 0x00, 83 | 0x00, 84 | ]); 85 | }); 86 | 87 | test('-18446744073709551616', () { 88 | final encoded = cbor.encode(BigInt.parse('-18446744073709551616')); 89 | expect(encoded, [0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); 90 | }); 91 | 92 | test('-18446744073709551617', () { 93 | final encoded = cbor.encode(BigInt.parse('-18446744073709551617')); 94 | expect(encoded, [ 95 | 0xc3, 96 | 0x49, 97 | 0x01, 98 | 0x00, 99 | 0x00, 100 | 0x00, 101 | 0x00, 102 | 0x00, 103 | 0x00, 104 | 0x00, 105 | 0x00, 106 | ]); 107 | }); 108 | 109 | test('-1', () { 110 | final encoded = cbor.encode(-1); 111 | expect(encoded, [0x20]); 112 | }); 113 | 114 | test('-10', () { 115 | final encoded = cbor.encode(-10); 116 | expect(encoded, [0x29]); 117 | }); 118 | 119 | test('-100', () { 120 | final encoded = cbor.encode(-100); 121 | expect(encoded, [0x38, 0x63]); 122 | }); 123 | 124 | test('-1000', () { 125 | final encoded = cbor.encode(-1000); 126 | expect(encoded, [0x39, 0x03, 0xe7]); 127 | }); 128 | 129 | test('0.0', () { 130 | final encoded = cbor.encode(0.0); 131 | expect(encoded, [0xf9, 0x00, 0x00]); 132 | }); 133 | 134 | test('-0.0', () { 135 | final encoded = cbor.encode(-0.0); 136 | expect(encoded, [0xf9, 0x80, 0x00]); 137 | }); 138 | 139 | test('1.0', () { 140 | final encoded = cbor.encode(1.0); 141 | expect(encoded, [0xf9, 0x3c, 0x00]); 142 | }); 143 | 144 | test('1.5', () { 145 | final encoded = cbor.encode(1.5); 146 | expect(encoded, [0xf9, 0x3e, 0x00]); 147 | }); 148 | 149 | test('65504.0', () { 150 | final encoded = cbor.encode(65504.0); 151 | expect(encoded, [0xf9, 0x7b, 0xff]); 152 | }); 153 | 154 | test('100000.0', () { 155 | final encoded = cbor.encode(100000.0); 156 | expect(encoded, [0xfa, 0x47, 0xc3, 0x50, 0x00]); 157 | }); 158 | 159 | test('3.4028234663852886e+38', () { 160 | final encoded = cbor.encode(3.4028234663852886e+38); 161 | expect(encoded, [0xfa, 0x7f, 0x7f, 0xff, 0xff]); 162 | }); 163 | 164 | test('1.0e+300', () { 165 | final encoded = cbor.encode(1.0e+300); 166 | expect(encoded, [0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c]); 167 | }); 168 | 169 | test('5.960464477539063e-8', () { 170 | final encoded = cbor.encode(5.960464477539063e-8); 171 | expect(encoded, [0xf9, 0x00, 0x01]); 172 | }); 173 | 174 | test('0.00006103515625', () { 175 | final encoded = cbor.encode(0.00006103515625); 176 | expect(encoded, [0xf9, 0x04, 0x00]); 177 | }); 178 | 179 | test('-4.0', () { 180 | final encoded = cbor.encode(-4.0); 181 | expect(encoded, [0xf9, 0xc4, 0x00]); 182 | }); 183 | 184 | test('-4.1', () { 185 | final encoded = cbor.encode(-4.1); 186 | expect(encoded, [0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66]); 187 | }); 188 | 189 | test('Infinity', () { 190 | final encoded = cbor.encode(double.infinity); 191 | expect(encoded, [0xf9, 0x7c, 0x00]); 192 | }); 193 | 194 | test('Nan', () { 195 | final encoded = cbor.encode(double.nan); 196 | expect(encoded, [0xf9, 0x7e, 0x00]); 197 | }); 198 | 199 | test('-Infinity', () { 200 | final encoded = cbor.encode(double.negativeInfinity); 201 | expect(encoded, [0xf9, 0xfc, 0x00]); 202 | }); 203 | 204 | test('False', () { 205 | final encoded = cbor.encode(false); 206 | expect(encoded, [0xf4]); 207 | }); 208 | 209 | test('True', () { 210 | final encoded = cbor.encode(true); 211 | expect(encoded, [0xf5]); 212 | }); 213 | 214 | test('Null', () { 215 | final encoded = cbor.encode(null); 216 | expect(encoded, [0xf6]); 217 | }); 218 | 219 | test('Tag (0) Date Time', () { 220 | final encoded = cbor.encode(DateTime.parse('2013-03-21T20:04:00Z')); 221 | expect(encoded, [ 222 | 0xc0, 223 | 0x74, 224 | 0x32, 225 | 0x30, 226 | 0x31, 227 | 0x33, 228 | 0x2d, 229 | 0x30, 230 | 0x33, 231 | 0x2d, 232 | 0x32, 233 | 0x31, 234 | 0x54, 235 | 0x32, 236 | 0x30, 237 | 0x3a, 238 | 0x30, 239 | 0x34, 240 | 0x3a, 241 | 0x30, 242 | 0x30, 243 | 0x5a, 244 | ]); 245 | }); 246 | 247 | test('Tag (1) Int', () { 248 | final encoded = cbor.encode( 249 | DateTime.fromMillisecondsSinceEpoch(1363896240000, isUtc: true), 250 | dateTimeEpoch: true, 251 | ); 252 | expect(encoded, [0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0]); 253 | }); 254 | 255 | test('Tag (1) Float', () { 256 | final encoded = cbor.encode( 257 | DateTime.fromMillisecondsSinceEpoch(1363896240500, isUtc: true), 258 | dateTimeEpoch: true, 259 | ); 260 | 261 | expect(encoded, [ 262 | 0xc1, 263 | 0xfb, 264 | 0x41, 265 | 0xd4, 266 | 0x52, 267 | 0xd9, 268 | 0xec, 269 | 0x20, 270 | 0x00, 271 | 0x00, 272 | ]); 273 | }); 274 | 275 | test('Tag (32) URI', () { 276 | final encoded = cbor.encode(Uri.parse('http://www.example.com')); 277 | expect(encoded, [ 278 | 0xd8, 279 | 0x20, 280 | 0x76, 281 | 0x68, 282 | 0x74, 283 | 0x74, 284 | 0x70, 285 | 0x3a, 286 | 0x2f, 287 | 0x2f, 288 | 0x77, 289 | 0x77, 290 | 0x77, 291 | 0x2e, 292 | 0x65, 293 | 0x78, 294 | 0x61, 295 | 0x6d, 296 | 0x70, 297 | 0x6c, 298 | 0x65, 299 | 0x2e, 300 | 0x63, 301 | 0x6f, 302 | 0x6d, 303 | ]); 304 | }); 305 | 306 | test('Empty single quotes', () { 307 | final encoded = cbor.encode(Uint8List.fromList([])); 308 | expect(encoded, [0x40]); 309 | }); 310 | 311 | test('4 bytes', () { 312 | final encoded = cbor.encode(Uint8List.fromList([0x1, 0x2, 0x3, 0x4])); 313 | expect(encoded, [0x44, 0x1, 0x2, 0x3, 0x4]); 314 | }); 315 | 316 | test('Empty double quote', () { 317 | final encoded = cbor.encode(''); 318 | expect(encoded, [0x60]); 319 | }); 320 | 321 | test('"a"', () { 322 | final encoded = cbor.encode('a'); 323 | expect(encoded, [0x61, 0x61]); 324 | }); 325 | 326 | test('"IETF"', () { 327 | final encoded = cbor.encode('IETF'); 328 | expect(encoded, [0x64, 0x49, 0x45, 0x54, 0x46]); 329 | }); 330 | 331 | test('Quoted backslash', () { 332 | final encoded = cbor.encode('"\\'); 333 | expect(encoded, [0x62, 0x22, 0x5c]); 334 | }); 335 | 336 | test('Unicode ü', () { 337 | final encoded = cbor.encode('ü'); 338 | expect(encoded, [0x62, 0xc3, 0xbc]); 339 | }); 340 | 341 | test('Unicode 水', () { 342 | final encoded = cbor.encode('水'); 343 | expect(encoded, [0x63, 0xe6, 0xb0, 0xb4]); 344 | }); 345 | 346 | test('Unicode 𐅑', () { 347 | final encoded = cbor.encode('𐅑'); 348 | expect(encoded, [0x64, 0xf0, 0x90, 0x85, 0x91]); 349 | }); 350 | 351 | test('Array empty', () { 352 | final encoded = cbor.encode([]); 353 | expect(encoded, [0x80]); 354 | }); 355 | 356 | test('Array 1,2,3', () { 357 | final encoded = cbor.encode([1, 2, 3]); 358 | expect(encoded, [0x83, 0x01, 0x02, 0x03]); 359 | }); 360 | 361 | test('Array 1,[2,3],[4,5]', () { 362 | final encoded = cbor.encode([ 363 | 1, 364 | [2, 3], 365 | [4, 5], 366 | ]); 367 | expect(encoded, [0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05]); 368 | }); 369 | 370 | test('Array 1..25', () { 371 | final encoded = cbor.encode(List.generate(25, (index) => index + 1)); 372 | expect(encoded, [ 373 | 0x98, 374 | 0x19, 375 | 0x01, 376 | 0x02, 377 | 0x03, 378 | 0x04, 379 | 0x05, 380 | 0x06, 381 | 0x07, 382 | 0x08, 383 | 0x09, 384 | 0x0a, 385 | 0x0b, 386 | 0x0c, 387 | 0x0d, 388 | 0x0e, 389 | 0x0f, 390 | 0x10, 391 | 0x11, 392 | 0x12, 393 | 0x13, 394 | 0x14, 395 | 0x15, 396 | 0x16, 397 | 0x17, 398 | 0x18, 399 | 0x18, 400 | 0x18, 401 | 0x19, 402 | ]); 403 | }); 404 | 405 | test('{}', () { 406 | final encoded = cbor.encode({}); 407 | expect(encoded, [0xa0]); 408 | }); 409 | 410 | test('{1:2,3:4}', () { 411 | final encoded = cbor.encode({1: 2, 3: 4}); 412 | expect(encoded, [0xa2, 0x01, 0x02, 0x03, 0x04]); 413 | }); 414 | 415 | test('{a:1,b:[2,3]}', () { 416 | final encoded = cbor.encode({ 417 | 'a': 1, 418 | 'b': [2, 3], 419 | }); 420 | expect(encoded, [0xa2, 0x61, 0x61, 0x01, 0x061, 0x62, 0x82, 0x02, 0x03]); 421 | }); 422 | 423 | test('[a, {b:c}]', () { 424 | final encoded = cbor.encode([ 425 | 'a', 426 | {'b': 'c'}, 427 | ]); 428 | expect(encoded, [0x82, 0x61, 0x61, 0xa1, 0x61, 0x62, 0x61, 0x63]); 429 | }); 430 | 431 | test('{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}', () { 432 | final encoded = cbor.encode({ 433 | 'a': 'A', 434 | 'b': 'B', 435 | 'c': 'C', 436 | 'd': 'D', 437 | 'e': 'E', 438 | }); 439 | expect(encoded, [ 440 | 0xa5, 441 | 0x61, 442 | 0x61, 443 | 0x61, 444 | 0x41, 445 | 0x61, 446 | 0x62, 447 | 0x61, 448 | 0x42, 449 | 0x61, 450 | 0x63, 451 | 0x61, 452 | 0x43, 453 | 0x61, 454 | 0x64, 455 | 0x61, 456 | 0x44, 457 | 0x61, 458 | 0x65, 459 | 0x61, 460 | 0x45, 461 | ]); 462 | }); 463 | }); 464 | } 465 | -------------------------------------------------------------------------------- /test/issues/issue62/decoded.txt: -------------------------------------------------------------------------------- 1 | {"op":"publish","topic":"/map","msg":{"header":{"stamp":{"sec":2235,"nanosec":435000000},"frame_id":"map"},"info":{"map_load_time":{"sec":2235,"nanosec":435000000},"resolution":0.05000000074505806,"width":93,"height":115,"origin":{"position":{"x":-1.2026861190795899,"y":-2.3811767578125003,"z":0.0},"orientation":{"x":0.0,"y":0.0,"z":0.0,"w":1.0}}},"data":"_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________zo8Pjo7PDw5OTs7PTo7Mzo5OTozODgyOTgyNjYy_____________________________________________________________________________________11hYF9gYGBeXV1fYV9fTFtbW11JW1tGUFlGUlQ0__________________________________________________________________________________8fQVZXV1dXVlZWVlZWVlZVVkpVVlZVVFVVAFJVI182__________________________________________________________________________________8AVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACn__________________________________________________________________________________ycAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP___________________________________________________________________________________wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo_____________________________________________________________________________________wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj___________________________________________________________________________________8yDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP____________________________________________________________________________________9EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv_____________________________________________________________________________________zJYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACzH______________________________________________________________________________________wsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo______________________________________________________________________________9CQ0NDQ0NDQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj______________________________________________________________________________xpiYmJiYmJiYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP______________________________________________________________________________NxFWWFhYWFdXVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo________________________________________________________________________________VlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj_______________________________________________________________________________8vYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALP________________________________________________________________________________8kVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAox_________________________________________________________________________________0FfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJDL__________________________________________________________________________________2I1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo____________________________________________________________________________________QmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj___________________________________________________________________________________9CYlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP____________________________________________________________________________________9iWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo_____________________________________________________________________________________0JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACz_____________________________________________________________________________________OmJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJMf______________________________________________________________________________________X2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAox______________________________________________________________________________________9CYlkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ_________________________________________________________________________________________9iYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn_________________________________________________________________________________________0FiSQAAAAAAAAAAAAAAAAAAAAAAAAAAACf______________________y9DOjL_____________________________________________________________NGJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ_____________________8YCRZiSDP_________________________MzL_________________________________TGIAAAAAAAAAAAAAAAAAAAAAAAAAXmI4__________________8cCQkAAFpgN____________________xsHBwdCSzP_______________________________88YloAAAAAAAAAAAAAAAAAAAAAAAAAXlk0______________8dCgkAAAAAAF5aNP____________8lFggHBwAAAABeYDj______________________________zJhYgAAAAAAAAAAAAAAAAAAAAAAAAAAXmE3____________CwkAAAAAAAAAAF5iN________xsIBwcAAAAAAAAAAABdVjT______________________________0ViWQAAAAAAAAAAAAAAAAAAAAAAAAAAUmJHQT0y____GgoJAAAAAAAAAAAAAFBiSDMcCQgICAAAAAAAAAAAAAAAAABMYj47Mv__________________________OWJiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFhYmE3HgsKAAAAAAAAAAAAAAAAAABPWBoAAAAAAAAAAAAAAAAAAAAAAAAATSheNv__________________________WmJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjX1MSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbN_________________________9BYl4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDH_______________________________9iYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALMf_______________________________________0FiXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsx________________________________________________M2JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACzH_________________________________________________________SmJaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKLzL_______________________________________________________________83YmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAonMf________________________________________________________________________9XYlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRMx_________________________________________________________________________________zpiXQAAAAAAAAAAAAAAAAAAAAAAAAAAEzH__________________________________________________________________________________________19iAAAAAAAAAAAAAAAAAAAAAAAAAAAABAsLCwsLGyIx________________________________________________________________________________QWJaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCgoKCgoZHzH___________________________________________________________________8yYmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCQkJCRgbMf________________________________________________________9CYlkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkJCQkJFxsx______________________________________________9iXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgICAcVGTH__________________________________0FiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBwcHBxQZMTL_____________________N2JaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGQkUy________________WWIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAF1hYTj_FQAAAAAAAAAAAAAAAAAAAABdYGA4FQAAAAAAAAAAAAAAAAAAAAAAXWA4______________87YloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgFAAAAAAAAXWI-Mv_______wAAAAAAAAAAAAAAAFxiOP__________FQAAAAAAAAAAAABcYjj_______________9hYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwFQAAAAAAXGE4_____________wAAAAAAAAAAAFxdNv__________________FQAAAABZWTX_______________9gYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMf8AAAAAXF43_________________xUAAAAAAFxfN_____________________________________________84YmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJv__AAAATmAjMf___________________xUAAFdhOP______________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD__wAAABkAADD_______________________9AM________________________________________________2JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsx__8AAAAAAAAw_________________________________________________________________________1xiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk____AAAAAAAAGjH_______________________________________________________________________9gYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMP___xIAAAAAAAAw______________________________________________________________________9KYl8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACTH___8aAAAAAAAAMP______________________________________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACP______wAAAAAAABkx_____________________________________________________________________2JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw______8AAAAAAAAAMP___________________________________________________________________2BiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJMf______AAAAAAAAADD___________________________________________________________________9hYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI________wAAAAAAAAAcMf________________________________________________________________9HYgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD_______8AAAAAAAAAADD_________________________________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgx________FQAAAAAAAAAw________________________________________________________________NWJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj_________zAAAAAAAAAAHjH______________________________________________________________19iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMP__________AAAAAAAAAAAw______________________________________________________________9iYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzH__________wAAAAAAAAAAMP____________________________________________________________9fYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACP___________8AAAAAAAAAACAx____________________________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUw____________AAAAAAAAAAAAMP__________________________________________________________P2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVXwAq_____________wAAAAAAAAAANzL__________________________________________________________19iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtiYWA5______________8VAAAAAABbYTn__________________________________________________________zNiAAAAAAAAAAAAAAAAAAAAAAAAAAAAWmJRNP___________________wAAAFpiQDL___________________________________________________________9eYgAAAAAAAAAAAAAAAAAAAAAAAAAAWkkz______________________8AAFpgOP______________________________________________________________YF8AAAAAAAAAAAAAAAAAAAAAAAAATTf_________________________AFlOM________________________________________________________________2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAw_________________________19cN________________________________________________________________2BiAAAAAAAAAAAAAAAAAAAAAAAAAAAh______________________________________________________________________________________________9hAAAAAAAAAAAAAAAAAAAAAAAAAAAAMP____________________________________________________________________________________________9ZYgAAAAAAAAAAAAAAAAAAAAAAAAAAIf______________________________________________________________________________________________YQAAAAAAAAAAAAAAAAAAAAAAAAAACDH_____________________________________________________________________________________________VWIAAAAAAAAAAAAAAAAAAAAAAAAAACP______________________________________________________________________________________________1tiAAAAAAAAAAAAAAAAAAAAAAAAAB0y______________________________________________________________________________________________9iAAAAAAAAAAAAAAAAAAAAAAAAAAAv______________________________________________________________________________________________9gBQAAAAAAAAAAAAAAAAAAAAAAAAAh________________________________________________________________________________________________YAAAAAAAAAAAAAAAAAAAAAAAAAAAMP______________________________________________________________________________________________N2EAAAAAAAAAAAAAAAAAAAAAAAAAIf_______________________________________________________________________________________________1lfAAAAAAAAAAAAAAAAAAAAAAAAADD_______________________________________________________________________________________________9dAAAAAAAAAAAAAAAAAAAAAAAAACD_______________________________________________________________________________________________8FVwAAAAAAAAAAAAAAAAAAAAAAAAAw________________________________________________________________________________________________AAAAAAAAAAAAAAAAAAAAAAAAAAAg________________________________________________________________________________________________MVYAAAAAAAAAAAAAAAAAAAAAAAAAMP_______________________________________________________________________________________________wBVX19fX19fXgAAAAAAAAAAAAAAH________________________________________________________________________________________________y9gYGBgYGBgYAAAAAAAAAAAAAAAAzH__________________________________________________________________________________________________________2EAAAAAAAAAAAAAACD__________________________________________________________________________________________________________2AAAAAAAAAAAAAAAA8x__________________________________________________________________________________________________________8AAAAAAAAAAAAAAAAo____________________________________________________________________________________________________________SwAAAAAAAAAAAAAf____________________________________________________________________________________________________________YAAAAAAAAAAAAAAAMP___________________________________________________________________________________________________________wAAAAAAAAAAAAAAH_____________________________________________________________________________________________________________8AAAAAAAAAAAAAADD___________________________________________________________________________________________________________9dAAAAAAAAAAAAAB____________________________________________________________________________________________________________9fAAAAAAAAAAAAADAy_____________________________________________________________________________________________________________2JiYgBZYmJiAFo7_____________________________________________________________________________________________________________19gXl9gYGBgWV85________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________"}} -------------------------------------------------------------------------------- /test/issues/issue62/jsonconverted.txt: -------------------------------------------------------------------------------- 1 | /home/steve/Development/google/dart/dart-sdk/bin/dart --no-serve-devtools run test -r json /home/steve/Development/google/dart/projects/cbor/test/issue62/issue62_test.dart -N "Weird JSON conversion" 2 | Testing started at 11:00 ... 3 | {"op":"publish","topic":"/map","msg":{"header":{"stamp":{"sec":2235,"nanosec":435000000},"frame_id":"map"},"info":{"map_load_time":{"sec":2235,"nanosec":435000000},"resolution":0.05000000074505806,"width":93,"height":115,"origin":{"position":{"x":-1.2026861190795899,"y":-2.3811767578125003,"z":0.0},"orientation":{"x":0.0,"y":0.0,"z":0.0,"w":1.0}}},"data":"_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________zo8Pjo7PDw5OTs7PTo7Mzo5OTozODgyOTgyNjYy_____________________________________________________________________________________11hYF9gYGBeXV1fYV9fTFtbW11JW1tGUFlGUlQ0__________________________________________________________________________________8fQVZXV1dXVlZWVlZWVlZVVkpVVlZVVFVVAFJVI182__________________________________________________________________________________8AVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACn__________________________________________________________________________________ycAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP___________________________________________________________________________________wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo_____________________________________________________________________________________wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj___________________________________________________________________________________8yDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP____________________________________________________________________________________9EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv_____________________________________________________________________________________zJYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACzH______________________________________________________________________________________wsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo______________________________________________________________________________9CQ0NDQ0NDQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj______________________________________________________________________________xpiYmJiYmJiYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP______________________________________________________________________________NxFWWFhYWFdXVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo________________________________________________________________________________VlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj_______________________________________________________________________________8vYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALP________________________________________________________________________________8kVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAox_________________________________________________________________________________0FfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJDL__________________________________________________________________________________2I1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo____________________________________________________________________________________QmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACj___________________________________________________________________________________9CYlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKP____________________________________________________________________________________9iWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo_____________________________________________________________________________________0JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACz_____________________________________________________________________________________OmJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJMf______________________________________________________________________________________X2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAox______________________________________________________________________________________9CYlkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ_________________________________________________________________________________________9iYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn_________________________________________________________________________________________0FiSQAAAAAAAAAAAAAAAAAAAAAAAAAAACf______________________y9DOjL_____________________________________________________________NGJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ_____________________8YCRZiSDP_________________________MzL_________________________________TGIAAAAAAAAAAAAAAAAAAAAAAAAAXmI4__________________8cCQkAAFpgN____________________xsHBwdCSzP_______________________________88YloAAAAAAAAAAAAAAAAAAAAAAAAAXlk0______________8dCgkAAAAAAF5aNP____________8lFggHBwAAAABeYDj______________________________zJhYgAAAAAAAAAAAAAAAAAAAAAAAAAAXmE3____________CwkAAAAAAAAAAF5iN________xsIBwcAAAAAAAAAAABdVjT______________________________0ViWQAAAAAAAAAAAAAAAAAAAAAAAAAAUmJHQT0y____GgoJAAAAAAAAAAAAAFBiSDMcCQgICAAAAAAAAAAAAAAAAABMYj47Mv__________________________OWJiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFhYmE3HgsKAAAAAAAAAAAAAAAAAABPWBoAAAAAAAAAAAAAAAAAAAAAAAAATSheNv__________________________WmJZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjX1MSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbN_________________________9BYl4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDH_______________________________9iYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALMf_______________________________________0FiXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsx________________________________________________M2JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACzH_________________________________________________________SmJaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKLzL_______________________________________________________________83YmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAonMf________________________________________________________________________9XYlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRMx_________________________________________________________________________________zpiXQAAAAAAAAAAAAAAAAAAAAAAAAAAEzH__________________________________________________________________________________________19iAAAAAAAAAAAAAAAAAAAAAAAAAAAABAsLCwsLGyIx________________________________________________________________________________QWJaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCgoKCgoZHzH___________________________________________________________________8yYmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCQkJCRgbMf________________________________________________________9CYlkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkJCQkJFxsx______________________________________________9iXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgICAcVGTH__________________________________0FiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBwcHBxQZMTL_____________________N2JaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGQkUy________________WWIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAF1hYTj_FQAAAAAAAAAAAAAAAAAAAABdYGA4FQAAAAAAAAAAAAAAAAAAAAAAXWA4______________87YloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgFAAAAAAAAXWI-Mv_______wAAAAAAAAAAAAAAAFxiOP__________FQAAAAAAAAAAAABcYjj_______________9hYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwFQAAAAAAXGE4_____________wAAAAAAAAAAAFxdNv__________________FQAAAABZWTX_______________9gYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMf8AAAAAXF43_________________xUAAAAAAFxfN_____________________________________________84YmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJv__AAAATmAjMf___________________xUAAFdhOP______________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD__wAAABkAADD_______________________9AM________________________________________________2JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsx__8AAAAAAAAw_________________________________________________________________________1xiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk____AAAAAAAAGjH_______________________________________________________________________9gYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMP___xIAAAAAAAAw______________________________________________________________________9KYl8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACTH___8aAAAAAAAAMP______________________________________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACP______wAAAAAAABkx_____________________________________________________________________2JiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw______8AAAAAAAAAMP___________________________________________________________________2BiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJMf______AAAAAAAAADD___________________________________________________________________9hYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI________wAAAAAAAAAcMf________________________________________________________________9HYgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD_______8AAAAAAAAAADD_________________________________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgx________FQAAAAAAAAAw________________________________________________________________NWJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj_________zAAAAAAAAAAHjH______________________________________________________________19iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMP__________AAAAAAAAAAAw______________________________________________________________9iYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzH__________wAAAAAAAAAAMP____________________________________________________________9fYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACP___________8AAAAAAAAAACAx____________________________________________________________YGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUw____________AAAAAAAAAAAAMP__________________________________________________________P2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVXwAq_____________wAAAAAAAAAANzL__________________________________________________________19iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtiYWA5______________8VAAAAAABbYTn__________________________________________________________zNiAAAAAAAAAAAAAAAAAAAAAAAAAAAAWmJRNP___________________wAAAFpiQDL___________________________________________________________9eYgAAAAAAAAAAAAAAAAAAAAAAAAAAWkkz______________________8AAFpgOP______________________________________________________________YF8AAAAAAAAAAAAAAAAAAAAAAAAATTf_________________________AFlOM________________________________________________________________2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAw_________________________19cN________________________________________________________________2BiAAAAAAAAAAAAAAAAAAAAAAAAAAAh______________________________________________________________________________________________9hAAAAAAAAAAAAAAAAAAAAAAAAAAAAMP____________________________________________________________________________________________9ZYgAAAAAAAAAAAAAAAAAAAAAAAAAAIf______________________________________________________________________________________________YQAAAAAAAAAAAAAAAAAAAAAAAAAACDH_____________________________________________________________________________________________VWIAAAAAAAAAAAAAAAAAAAAAAAAAACP______________________________________________________________________________________________1tiAAAAAAAAAAAAAAAAAAAAAAAAAB0y______________________________________________________________________________________________9iAAAAAAAAAAAAAAAAAAAAAAAAAAAv______________________________________________________________________________________________9gBQAAAAAAAAAAAAAAAAAAAAAAAAAh________________________________________________________________________________________________YAAAAAAAAAAAAAAAAAAAAAAAAAAAMP______________________________________________________________________________________________N2EAAAAAAAAAAAAAAAAAAAAAAAAAIf_______________________________________________________________________________________________1lfAAAAAAAAAAAAAAAAAAAAAAAAADD_______________________________________________________________________________________________9dAAAAAAAAAAAAAAAAAAAAAAAAACD_______________________________________________________________________________________________8FVwAAAAAAAAAAAAAAAAAAAAAAAAAw________________________________________________________________________________________________AAAAAAAAAAAAAAAAAAAAAAAAAAAg________________________________________________________________________________________________MVYAAAAAAAAAAAAAAAAAAAAAAAAAMP_______________________________________________________________________________________________wBVX19fX19fXgAAAAAAAAAAAAAAH________________________________________________________________________________________________y9gYGBgYGBgYAAAAAAAAAAAAAAAAzH__________________________________________________________________________________________________________2EAAAAAAAAAAAAAACD__________________________________________________________________________________________________________2AAAAAAAAAAAAAAAA8x__________________________________________________________________________________________________________8AAAAAAAAAAAAAAAAo____________________________________________________________________________________________________________SwAAAAAAAAAAAAAf____________________________________________________________________________________________________________YAAAAAAAAAAAAAAAMP___________________________________________________________________________________________________________wAAAAAAAAAAAAAAH_____________________________________________________________________________________________________________8AAAAAAAAAAAAAADD___________________________________________________________________________________________________________9dAAAAAAAAAAAAAB____________________________________________________________________________________________________________9fAAAAAAAAAAAAADAy_____________________________________________________________________________________________________________2JiYgBZYmJiAFo7_____________________________________________________________________________________________________________19gXl9gYGBgWV85________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________"}} 4 | 5 | Process finished with exit code 0 6 | -------------------------------------------------------------------------------- /lib/src/value/string.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Package : Cbor 3 | * Author : S. Hamblett 4 | * Date : 04/01/2022 5 | * Copyright : S.Hamblett 6 | */ 7 | 8 | import 'dart:convert'; 9 | 10 | import 'package:cbor/cbor.dart'; 11 | import 'package:collection/collection.dart'; 12 | 13 | import '../encoder/sink.dart'; 14 | import '../utils/arg.dart'; 15 | import '../utils/utils.dart'; 16 | import 'internal.dart'; 17 | 18 | /// A CBOR string encoded in UTF-8. 19 | abstract class CborString extends CborValue { 20 | /// Returns the UTF-8 value of this. 21 | List get utf8Bytes; 22 | 23 | CborLengthType get type; 24 | 25 | factory CborString(String string, {List tags}) = _CborStringImpl; 26 | 27 | factory CborString.fromUtf8(List value, {List tags}) = 28 | _CborStringImpl.fromUtf8; 29 | 30 | factory CborString.indefinite(List string, {List tags}) = 31 | _CborIndefiniteLengthStringImpl; 32 | 33 | factory CborString.indefiniteFromUtf8( 34 | List> value, { 35 | List tags, 36 | }) = _CborIndefiniteLengthStringImpl.fromUtf8; 37 | 38 | /// Convert to [String]. 39 | /// 40 | /// Throws [FormatException] if [allowMalformed] is false and the value 41 | /// is not valid UTF-8. 42 | @override 43 | String toString({bool allowMalformed = false}); 44 | } 45 | 46 | class _CborStringImpl with CborValueMixin implements CborString { 47 | @override 48 | final List tags; 49 | @override 50 | final CborLengthType type = CborLengthType.definite; 51 | 52 | String? _string; 53 | List? _utf8; 54 | 55 | @override 56 | int get hashCode => Object.hashAll([utf8Bytes, tags].flattened); 57 | 58 | @override 59 | List get utf8Bytes { 60 | _utf8 ??= utf8.encode(_string!); 61 | 62 | return _utf8!; 63 | } 64 | 65 | _CborStringImpl(this._string, {this.tags = const []}); 66 | _CborStringImpl.fromUtf8(this._utf8, {this.tags = const []}); 67 | 68 | @override 69 | String toString({bool allowMalformed = false}) { 70 | _string ??= 71 | allowMalformed 72 | ? const Utf8Decoder(allowMalformed: true).convert(_utf8!) 73 | : const Utf8Decoder(allowMalformed: false).convert(_utf8!); 74 | 75 | return _string!; 76 | } 77 | 78 | @override 79 | bool operator ==(Object other) => 80 | other is CborString && 81 | tags.equals(other.tags) && 82 | utf8Bytes.equals(other.utf8Bytes); 83 | 84 | @override 85 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 86 | return toString(allowMalformed: o.allowMalformedUtf8); 87 | } 88 | 89 | @override 90 | void encode(EncodeSink sink) { 91 | CborEncodeDefiniteLengthString(this).encode(sink); 92 | } 93 | 94 | @override 95 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 96 | return toString(); 97 | } 98 | } 99 | 100 | class _CborIndefiniteLengthStringImpl 101 | with CborValueMixin 102 | implements CborString { 103 | @override 104 | final List tags; 105 | @override 106 | final CborLengthType type = CborLengthType.indefinite; 107 | 108 | List? _string; 109 | List>? _utf8; 110 | 111 | @override 112 | int get hashCode => Object.hashAll([utf8Bytes, tags].flattened); 113 | 114 | @override 115 | List get utf8Bytes { 116 | _utf8 ??= _string!.map(utf8.encode).toList(growable: false); 117 | 118 | return _utf8!.flattened.toList(growable: false); 119 | } 120 | 121 | _CborIndefiniteLengthStringImpl(this._string, {this.tags = const []}); 122 | _CborIndefiniteLengthStringImpl.fromUtf8(this._utf8, {this.tags = const []}); 123 | 124 | @override 125 | String toString({bool allowMalformed = false}) { 126 | _string ??= _utf8! 127 | .map(Utf8Decoder(allowMalformed: allowMalformed).convert) 128 | .toList(growable: false); 129 | 130 | return _string!.join(); 131 | } 132 | 133 | @override 134 | bool operator ==(Object other) => 135 | other is CborString && 136 | tags.equals(other.tags) && 137 | utf8Bytes.equals(other.utf8Bytes); 138 | 139 | @override 140 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 141 | return toString(allowMalformed: o.allowMalformedUtf8); 142 | } 143 | 144 | @override 145 | void encode(EncodeSink sink) { 146 | _string ??= _utf8! 147 | .map(const Utf8Decoder(allowMalformed: true).convert) 148 | .toList(growable: false); 149 | 150 | CborEncodeIndefiniteLengthString(_string!).encode(sink); 151 | } 152 | 153 | @override 154 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 155 | return toString(); 156 | } 157 | } 158 | 159 | /// Use this to force the [CborEncoder] to encode an indefinite length string. 160 | /// 161 | /// This is never generated by decoder. 162 | abstract class CborEncodeIndefiniteLengthString extends CborValue { 163 | factory CborEncodeIndefiniteLengthString( 164 | List items, { 165 | List tags, 166 | }) = _CborEncodeIndefiniteLengthStringImpl; 167 | } 168 | 169 | class _CborEncodeIndefiniteLengthStringImpl 170 | with CborValueMixin 171 | implements CborEncodeIndefiniteLengthString { 172 | final List items; 173 | @override 174 | final List tags; 175 | 176 | const _CborEncodeIndefiniteLengthStringImpl( 177 | this.items, { 178 | this.tags = const [], 179 | }); 180 | 181 | @override 182 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 183 | return CborString( 184 | items.join(), 185 | tags: tags, 186 | ).toObjectInternal(cyclicCheck, o); 187 | } 188 | 189 | @override 190 | void encode(EncodeSink sink) { 191 | sink.addTags(tags); 192 | 193 | sink.addHeaderInfo(CborMajorType.textString, Arg.indefiniteLength); 194 | 195 | for (final value in items) { 196 | CborEncodeDefiniteLengthString(CborString(value)).encode(sink); 197 | } 198 | 199 | (const Break()).encode(sink); 200 | } 201 | 202 | @override 203 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 204 | return CborString(items.join(), tags: tags).toJsonInternal(cyclicCheck, o); 205 | } 206 | } 207 | 208 | /// Use this to force the [CborEncoder] to encode an definite length string. 209 | /// 210 | /// This is never generated by decoder. 211 | abstract class CborEncodeDefiniteLengthString extends CborValue { 212 | factory CborEncodeDefiniteLengthString(CborString input) = 213 | _CborEncodeDefiniteLengthStringImpl; 214 | } 215 | 216 | class _CborEncodeDefiniteLengthStringImpl 217 | with CborValueMixin 218 | implements CborEncodeDefiniteLengthString { 219 | final CborString inner; 220 | 221 | @override 222 | List get tags => inner.tags; 223 | const _CborEncodeDefiniteLengthStringImpl(this.inner); 224 | 225 | @override 226 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 227 | return inner.toObjectInternal(cyclicCheck, o); 228 | } 229 | 230 | @override 231 | Object? toJsonInternal(Set cyclicCheck, ToJsonOptions o) { 232 | return inner.toJsonInternal(cyclicCheck, o); 233 | } 234 | 235 | @override 236 | void encode(EncodeSink sink) { 237 | final bytes = utf8.encode(inner.toString()); 238 | 239 | sink.addTags(tags); 240 | 241 | sink.addHeaderInfo(CborMajorType.textString, Arg.int(bytes.length)); 242 | 243 | sink.add(bytes); 244 | } 245 | } 246 | 247 | /// A CBOR string which encodes a datetime. 248 | abstract class CborDateTimeString extends CborString implements CborDateTime { 249 | /// Create a date time string. 250 | /// 251 | /// If [timeZoneOffset] is not provided, the timezone for [value] is used. 252 | /// 253 | /// This will ommit second fraction if zero, and trim it depending on the 254 | /// resolution. 255 | factory CborDateTimeString( 256 | DateTime value, { 257 | Duration? timeZoneOffset, 258 | List tags, 259 | }) = _CborDateTimeStringImpl; 260 | 261 | factory CborDateTimeString.fromString(String str, {List tags}) = 262 | _CborDateTimeStringImpl.fromString; 263 | 264 | factory CborDateTimeString.fromUtf8(List str, {List tags}) = 265 | _CborDateTimeStringImpl.fromUtf8; 266 | } 267 | 268 | class _CborDateTimeStringImpl extends _CborStringImpl 269 | implements CborDateTimeString { 270 | DateTime? _datetime; 271 | 272 | _CborDateTimeStringImpl( 273 | DateTime value, { 274 | Duration? timeZoneOffset, 275 | List tags = const [CborTag.dateTimeString], 276 | }) : _datetime = value, 277 | super(value.toInternetIso8601String(timeZoneOffset), tags: tags); 278 | 279 | _CborDateTimeStringImpl.fromString( 280 | String super.str, { 281 | super.tags = const [CborTag.dateTimeString], 282 | }); 283 | 284 | _CborDateTimeStringImpl.fromUtf8( 285 | List super.str, { 286 | super.tags = const [CborTag.dateTimeString], 287 | }) : super.fromUtf8(); 288 | 289 | @override 290 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 291 | return o.parseDateTime ? toDateTime() : toString(); 292 | } 293 | 294 | @override 295 | DateTime toDateTime() { 296 | _datetime ??= DateTime.parse(toString()); 297 | return _datetime!; 298 | } 299 | } 300 | 301 | /// A CBOR string containing URI. 302 | abstract class CborUri extends CborString { 303 | factory CborUri.fromString(String value, {List tags}) = 304 | _CborUriImpl.fromString; 305 | 306 | factory CborUri.fromUtf8(List str, {List tags}) = 307 | _CborUriImpl.fromUtf8; 308 | 309 | factory CborUri(Uri value, {List tags}) = _CborUriImpl; 310 | 311 | /// Parse the URI, may throw [FormatException] if the URI is not valid. 312 | Uri parse(); 313 | } 314 | 315 | class _CborUriImpl extends _CborStringImpl implements CborUri { 316 | Uri? _value; 317 | 318 | _CborUriImpl.fromString( 319 | String super.value, { 320 | super.tags = const [CborTag.uri], 321 | }); 322 | 323 | _CborUriImpl(Uri value, {List tags = const [CborTag.uri]}) 324 | : _value = value, 325 | super(value.toString(), tags: tags); 326 | 327 | _CborUriImpl.fromUtf8( 328 | List super.value, { 329 | super.tags = const [CborTag.uri], 330 | }) : super.fromUtf8(); 331 | 332 | @override 333 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 334 | return o.parseUri ? parse() : toString(); 335 | } 336 | 337 | @override 338 | Uri parse() { 339 | _value ??= Uri.parse(toString()); 340 | return _value!; 341 | } 342 | } 343 | 344 | /// A CBOR string containing a base 64 value. 345 | abstract class CborBase64 extends CborString { 346 | factory CborBase64.fromString(String value, {List tags}) = 347 | _CborBase64Impl.fromString; 348 | 349 | factory CborBase64.encode(List bytes, {List tags}) = 350 | _CborBase64Impl.encode; 351 | 352 | factory CborBase64.fromUtf8(List str, {List tags}) = 353 | _CborBase64Impl.fromUtf8; 354 | 355 | List decode(); 356 | } 357 | 358 | class _CborBase64Impl extends _CborStringImpl implements CborBase64 { 359 | List? _value; 360 | 361 | _CborBase64Impl.fromString( 362 | String super.value, { 363 | super.tags = const [CborTag.base64], 364 | }); 365 | 366 | _CborBase64Impl.encode( 367 | List bytes, { 368 | List tags = const [CborTag.base64], 369 | }) : _value = bytes, 370 | super(base64.encode(bytes), tags: tags); 371 | 372 | _CborBase64Impl.fromUtf8( 373 | List super.str, { 374 | super.tags = const [CborTag.base64], 375 | }) : super.fromUtf8(); 376 | 377 | @override 378 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 379 | return o.decodeBase64 ? decode() : toString(); 380 | } 381 | 382 | @override 383 | List decode() { 384 | _value ??= base64.decode(base64.normalize(toString())); 385 | return _value!; 386 | } 387 | } 388 | 389 | /// A CBOR string containing a base 64 url safe value. 390 | abstract class CborBase64Url extends CborString { 391 | factory CborBase64Url.fromString(String value, {List tags}) = 392 | _CborBase64UrlImpl.fromString; 393 | 394 | factory CborBase64Url.encode(List bytes, {List tags}) = 395 | _CborBase64UrlImpl.encode; 396 | 397 | factory CborBase64Url.fromUtf8(List str, {List tags}) = 398 | _CborBase64UrlImpl.fromUtf8; 399 | 400 | /// Use [Base64Codec.urlSafe] to decode. 401 | List decode(); 402 | } 403 | 404 | class _CborBase64UrlImpl extends _CborStringImpl implements CborBase64Url { 405 | List? _value; 406 | 407 | _CborBase64UrlImpl.fromString( 408 | String super.value, { 409 | super.tags = const [CborTag.base64Url], 410 | }); 411 | 412 | _CborBase64UrlImpl.encode( 413 | List bytes, { 414 | List tags = const [CborTag.base64Url], 415 | }) : _value = bytes, 416 | super(base64Url.encode(bytes), tags: tags); 417 | 418 | _CborBase64UrlImpl.fromUtf8( 419 | List super.str, { 420 | super.tags = const [CborTag.base64Url], 421 | }) : super.fromUtf8(); 422 | 423 | @override 424 | Object? toObjectInternal(Set cyclicCheck, ToObjectOptions o) { 425 | return o.decodeBase64 ? decode() : toString(); 426 | } 427 | 428 | @override 429 | List decode() { 430 | _value ??= base64Url.decode(base64Url.normalize(toString())); 431 | return _value!; 432 | } 433 | } 434 | 435 | /// A CBOR string containing a regular expression. 436 | /// 437 | /// Does not provide any additional functionality currently. 438 | abstract class CborRegex extends CborString { 439 | factory CborRegex.fromString(String data, {List tags}) = 440 | _CborRegexImpl.fromString; 441 | 442 | factory CborRegex.fromUtf8(List str, {List tags}) = 443 | _CborRegexImpl.fromUtf8; 444 | } 445 | 446 | class _CborRegexImpl extends _CborStringImpl implements CborRegex { 447 | _CborRegexImpl.fromString( 448 | String super.data, { 449 | super.tags = const [CborTag.regex], 450 | }); 451 | 452 | _CborRegexImpl.fromUtf8( 453 | List super.str, { 454 | super.tags = const [CborTag.regex], 455 | }) : super.fromUtf8(); 456 | } 457 | 458 | /// A CBOR string containing a regular expression. 459 | /// 460 | /// Does not provide any additional functionality currently. 461 | abstract class CborMime extends CborString { 462 | factory CborMime.fromString(String data, {List tags}) = 463 | _CborMimeImpl.fromString; 464 | 465 | factory CborMime.fromUtf8(List str, {List tags}) = 466 | _CborMimeImpl.fromUtf8; 467 | } 468 | 469 | class _CborMimeImpl extends _CborStringImpl implements CborMime { 470 | _CborMimeImpl.fromString( 471 | String super.data, { 472 | super.tags = const [CborTag.mime], 473 | }); 474 | 475 | _CborMimeImpl.fromUtf8( 476 | List super.str, { 477 | super.tags = const [CborTag.mime], 478 | }) : super.fromUtf8(); 479 | } 480 | --------------------------------------------------------------------------------