Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature][BugFix] Added CBOR Rational Number and Fixed encoding of some Array sub-types #60

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/cbor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
/// * [CborRegex]
/// * [CborDateTimeFloat]
/// * [CborDecimalFraction]
/// * [CborRationalNumber]
/// * [CborBigFloat]
library cbor;

Expand Down
20 changes: 20 additions & 0 deletions lib/src/decoder/stage3.dart
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,26 @@ CborList _createList(
CborLengthType type,
) {
switch (raw.tags.lastWhereOrNull(isHintSubtype)) {
case CborTag.rationalNumber:
if (items.length != 2) {
break;
}

final numerator = items[0];
final denominator = items[1];

if (numerator is! CborInt ||
denominator is! CborInt ||
denominator.toInt() == 0) {
break;
}

return CborRationalNumber(
numerator: numerator,
denominator: denominator,
tags: raw.tags,
type: type,
);
case CborTag.decimalFraction:
if (items.length != 2) {
break;
Expand Down
93 changes: 91 additions & 2 deletions lib/src/value/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,88 @@ class _CborDecimalFractionImpl extends DelegatingList<CborValue>
@override
void encode(EncodeSink sink) {
sink.addTags(tags);
sink.addHeaderInfo(4, const Arg.int(2));
sink.addHeaderInfo(
4,
switch (type) {
CborLengthType.definite || CborLengthType.auto => const Arg.int(2),
CborLengthType.indefinite => Arg.indefiniteLength,
},
);
exponent.encode(sink);
mantissa.encode(sink);

if (type == CborLengthType.indefinite) {
(const Break()).encode(sink);
}
}
}

/// A CBOR rational number (m / n).
/// https://peteroupc.github.io/CBOR/rational.html
abstract class CborRationalNumber extends CborList {
factory CborRationalNumber({
required CborInt numerator,
required CborInt denominator,
List<int> tags,
CborLengthType type,
}) = _CborRationalNumberImpl;

CborInt get numerator;

CborInt get denominator;
}

class _CborRationalNumberImpl extends DelegatingList<CborValue>
with CborValueMixin
implements CborRationalNumber {
_CborRationalNumberImpl({
required this.numerator,
required this.denominator,
this.tags = const [CborTag.rationalNumber],
this.type = CborLengthType.auto,
}) : assert(denominator.toInt() != 0),
super(List.of([numerator, denominator], growable: false));

@override
final CborInt numerator;
@override
final CborInt denominator;

@override
final List<int> tags;

@override
final CborLengthType type;

@override
Object? toObjectInternal(Set<Object> cyclicCheck, ToObjectOptions o) {
return [numerator.toBigInt(), denominator.toBigInt()];
}

@override
Object? toJsonInternal(Set<Object> cyclicCheck, ToJsonOptions o) {
return [
numerator.toJsonInternal(cyclicCheck, o),
denominator.toJsonInternal(cyclicCheck, o),
];
}

@override
void encode(EncodeSink sink) {
sink.addTags(tags);
sink.addHeaderInfo(
4,
switch (type) {
CborLengthType.definite || CborLengthType.auto => const Arg.int(2),
CborLengthType.indefinite => Arg.indefiniteLength,
},
);
numerator.encode(sink);
denominator.encode(sink);

if (type == CborLengthType.indefinite) {
(const Break()).encode(sink);
}
}
}

Expand Down Expand Up @@ -286,9 +365,19 @@ class _CborBigFloatImpl extends DelegatingList<CborValue>
@override
void encode(EncodeSink sink) {
sink.addTags(tags);
sink.addHeaderInfo(4, const Arg.int(2));
sink.addHeaderInfo(
4,
switch (type) {
CborLengthType.definite || CborLengthType.auto => const Arg.int(2),
CborLengthType.indefinite => Arg.indefiniteLength,
},
);
exponent.encode(sink);
mantissa.encode(sink);

if (type == CborLengthType.indefinite) {
(const Break()).encode(sink);
}
}

@override
Expand Down
1 change: 1 addition & 0 deletions lib/src/value/value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CborTag {
static const int expectedConversionToBase64 = 22;
static const int expectedConversionToBase64Url = 21;
static const int expectedConversionToBase16 = 23;
static const int rationalNumber = 30;
static const int uri = 32;
static const int base64Url = 33;
static const int base64 = 34;
Expand Down
103 changes: 103 additions & 0 deletions test/rational_number_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Package : Cbor
* Author : Alex Dochioiu <[email protected]>
* Date : 19/06/2024
* Copyright : Alex Dochioiu
*/

import 'package:cbor/cbor.dart';
import 'package:test/test.dart';

void main() {
group(
"RationalNumber",
() => {
group('encoding tests', () {
test('encode auto length', () {
expect(
cbor.encode(
CborRationalNumber(
numerator: CborInt(BigInt.from(1)),
denominator: CborSmallInt(2),
type: CborLengthType.auto,
),
),
[0xd8, 0x1e, 0x82, 0x1, 0x2],
);
});

test('encode definite length', () {
expect(
cbor.encode(
CborRationalNumber(
numerator: CborInt(BigInt.from(1)),
denominator: CborSmallInt(2),
type: CborLengthType.definite,
),
),
[0xd8, 0x1e, 0x82, 0x1, 0x2],
);
});

test('encode indefinite length', () {
expect(
cbor.encode(
CborRationalNumber(
numerator: CborInt(BigInt.from(1)),
denominator: CborSmallInt(2),
type: CborLengthType.indefinite,
),
),
[0xd8, 0x1e, 0x9f, 0x1, 0x2, 0xff],
);
});
}),
group('decoding tests', () {
test('auto length', () {
expect(
cbor.decode([0xd8, 0x1e, 0x82, 0x1, 0x2]),
CborRationalNumber(
numerator: CborInt(BigInt.from(1)),
denominator: CborSmallInt(2),
type: CborLengthType.auto,
),
);
});

test('definite length', () {
expect(
cbor.decode([0xd8, 0x1e, 0x82, 0x1, 0x2]),
CborRationalNumber(
numerator: CborInt(BigInt.from(1)),
denominator: CborSmallInt(2),
type: CborLengthType.definite,
),
);
});

test('indefinite length', () {
expect(
cbor.decode([0xd8, 0x1e, 0x9f, 0x1, 0x2, 0xff]),
CborRationalNumber(
numerator: CborInt(BigInt.from(1)),
denominator: CborSmallInt(2),
type: CborLengthType.indefinite,
),
);
});

test('not CborRationalNumber when denominator is 0', () {
expect(
cbor.decode([0xd8, 0x1e, 0x9f, 0x1, 0x0, 0xff])
is CborRationalNumber,
false,
);

expect(
cbor.decode([0xd8, 0x1e, 0x9f, 0x1, 0x0, 0xff]) is CborList,
true,
);
});
}),
});
}
Loading