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

Stabilize Dart #50

Merged
merged 41 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e3112fc
Copy golang test code to Dart
temeddix Oct 4, 2024
9ac4d82
Make Dart commands work on Windows
temeddix Oct 4, 2024
04dcc95
Properly install Dart dependencies
temeddix Oct 4, 2024
b7b6334
Print Dart errors to console
temeddix Oct 4, 2024
c5bb36f
Improve readability by limiting line length
temeddix Oct 4, 2024
d3103b2
Ensure Dart error outputs are shown
temeddix Oct 4, 2024
ca97f24
Pass `Uint8List` properly to Dart
temeddix Oct 4, 2024
5e4b000
Make Dart test code run
temeddix Oct 4, 2024
2af88c7
Shorten code
temeddix Oct 4, 2024
9a2cef9
Implement `sortMapEntries`
temeddix Oct 4, 2024
d15ecf2
Reduce code duplication
temeddix Oct 4, 2024
e043942
Organize Dart writer code
temeddix Oct 4, 2024
ee013ca
Match Dart codeblock style
temeddix Oct 4, 2024
7127f5b
Change a test name
temeddix Oct 4, 2024
e1d2676
Update Dart test code as intended
temeddix Oct 4, 2024
63da582
Remove unused helper code
temeddix Oct 4, 2024
69a9336
Match the deserialization class to the original
temeddix Oct 5, 2024
6c91b63
Rename a test
temeddix Oct 5, 2024
eb737b6
Shorten an error message
temeddix Oct 6, 2024
a4ecef5
Match `serializeInt128` to `deserializeInt128`
temeddix Oct 6, 2024
a6138c3
Update `ComplexMap` test to enable proper nested comparison in Dart
temeddix Oct 6, 2024
0cf947a
Check if the representation is valid boolean
temeddix Oct 6, 2024
f00e43f
Fix the test related to map deserialization
temeddix Oct 7, 2024
cd5bd98
Shorten code
temeddix Oct 7, 2024
6dd6ef3
Check maximum sequence length
temeddix Oct 7, 2024
b2de40b
Consider max container depth
temeddix Oct 7, 2024
ff191ef
Use `Uint8List` while sorting map entries for performance
temeddix Oct 7, 2024
dd121a6
Fix `test_get_registry`
temeddix Oct 7, 2024
ff20b87
Print output on fail from `test_dart_runtime_autotest`
temeddix Oct 9, 2024
b022ded
Bump Dart versions
temeddix Oct 9, 2024
408c3e6
Make `sortMapEntries` an abstract function
temeddix Oct 9, 2024
9c92e8e
Organize `sortMapEntries` code
temeddix Oct 9, 2024
f36ad1e
Update expected positive sample count regarding `v13`
temeddix Oct 9, 2024
a7a9ae8
Format a YAML file
temeddix Oct 9, 2024
f5d1489
Write some temporary C# test code
temeddix Oct 9, 2024
e82a6c8
Revert "Write some temporary C# test code"
temeddix Oct 9, 2024
9535121
Update an expected sample number
temeddix Oct 9, 2024
0c07d41
Revert changes in `v13` test
temeddix Oct 24, 2024
10f6308
Exclude `v13` value from Dart tests
temeddix Oct 24, 2024
7dbb31a
Relieve Bincode bool check logic
temeddix Jan 4, 2025
eda2781
Revert "Relieve Bincode bool check logic"
temeddix Jan 4, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
- name: Setup Additional Languages (dart)
uses: dart-lang/setup-dart@v1
with:
sdk: 2.14.0
sdk: 3.5.3

- name: Setup Additional Languages (swift)
uses: swift-actions/setup-swift@v1
Expand Down
18 changes: 16 additions & 2 deletions serde-generate/runtime/dart/bcs/bcs_deserializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@

part of bcs;

// Maximum length allowed for sequences (vectors, bytes, strings) and maps.
const maxSequenceLength = (1 << 31) - 1;

// Maximum number of nested structs and enum variants.
const maxContainerDepth = 500;

class BcsDeserializer extends BinaryDeserializer {
BcsDeserializer(Uint8List input) : super(input);
BcsDeserializer(Uint8List input)
: super(
input: input,
containerDepthBudget: maxContainerDepth,
);

int deserializeUleb128AsUint32() {
var value = 0;
Expand All @@ -27,7 +37,11 @@ class BcsDeserializer extends BinaryDeserializer {

@override
int deserializeLength() {
return deserializeUleb128AsUint32();
final length = deserializeUleb128AsUint32();
if (length > maxSequenceLength) {
throw Exception("length is too large");
}
return length;
}

@override
Expand Down
47 changes: 45 additions & 2 deletions serde-generate/runtime/dart/bcs/bcs_serializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
part of bcs;

class BcsSerializer extends BinarySerializer {
BcsSerializer()
: super(
containerDepthBudget: maxContainerDepth,
);

void serializeUint32AsUleb128(int value) {
while (((value & 0xFFFFFFFF) >> 7) != 0) {
output.add((value & 0x7f) | 0x80);
Expand All @@ -22,7 +27,45 @@ class BcsSerializer extends BinarySerializer {
serializeUint32AsUleb128(value);
}

void sortMapEntries(Uint8List offsets) {
// TODO(#120)
void sortMapEntries(List<int> offsets) {
if (offsets.isEmpty) {
return;
}

// Prepare a list of slices
final data = Uint8List.fromList(output);
List<Uint8List> slices = [];

// Collect slices
for (int i = 0; i < offsets.length; i++) {
final int startOffset = offsets[i];
final int cutOffset;
if (i + 1 < offsets.length) {
cutOffset = offsets[i + 1];
} else {
cutOffset = data.length;
}
slices.add(data.sublist(startOffset, cutOffset));
}

// Sort slices using lexicographic comparison
slices.sort((a, b) {
for (int i = 0; i < a.length && i < b.length; i++) {
if (a[i] != b[i]) {
return a[i].compareTo(b[i]);
}
}
return a.length.compareTo(b.length);
});

// Write sorted slices back to output
int writePosition = offsets[0];
for (final slice in slices) {
output.setRange(writePosition, writePosition + slice.length, slice);
writePosition += slice.length;
}

// Ensure the final length is correct
assert(offsets.last == output.length);
}
}
10 changes: 6 additions & 4 deletions serde-generate/runtime/dart/bincode/bincode_deserializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@

part of bincode;

// Maximum number of nested structs and enum variants.
const maxContainerDepth = (1 << 31) - 1;

class BincodeDeserializer extends BinaryDeserializer {
BincodeDeserializer(Uint8List input) : super(input);
BincodeDeserializer(Uint8List input)
: super(input: input, containerDepthBudget: maxContainerDepth);

@override
int deserializeLength() {
// bincode sends this as a u64 but since transferred data length should never exceed the upper
// bounds of an i64 (9223372036854775807 bytes is 9k petabytes) still deserialize to a Dart int
return deserializeInt64();
return deserializeUint64().toInt();
}

@override
Expand Down
11 changes: 7 additions & 4 deletions serde-generate/runtime/dart/bincode/bincode_serializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
part of bincode;

class BincodeSerializer extends BinarySerializer {
BincodeSerializer()
: super(
containerDepthBudget: maxContainerDepth,
);

@override
void serializeLength(int value) {
// bincode expects a u64 but since the capacity of a Dart int is less than that
// we can safely serialize as int to simplify over constructing Uint8 bytes
return serializeInt64(value);
serializeUint64(Uint64(BigInt.from(value)));
}

@override
void serializeVariantIndex(int value) {
serializeUint32(value);
}

void sortMapEntries(Int32List offsets) {
void sortMapEntries(List<int> offsets) {
// Not required by the format.
}
}
4 changes: 2 additions & 2 deletions serde-generate/runtime/dart/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: serde

environment:
sdk: '>=2.14.0 <3.0.0'
sdk: '>=3.0.0 <4.0.0'

dependencies:
meta: ^1.0.0
tuple: ^2.0.0
dev_dependencies:
dev_dependencies:
test: ^1.19.3
29 changes: 26 additions & 3 deletions serde-generate/runtime/dart/serde/binary_deserializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,32 @@
part of serde;

abstract class BinaryDeserializer {
BinaryDeserializer(Uint8List input) : input = ByteData.view(input.buffer);
BinaryDeserializer({
required Uint8List input,
required this.containerDepthBudget,
}) : input = ByteData.view(input.buffer);

@protected
final ByteData input;
int _offset = 0;
int containerDepthBudget;

int get offset {
return _offset;
}

bool deserializeBool() {
final result = input.getUint8(_offset) != 0;
final result = input.getUint8(_offset);
ma2bd marked this conversation as resolved.
Show resolved Hide resolved
_offset += 1;
return result;
if (result == 0) {
return false;
} else if (result == 1) {
return true;
} else {
throw Exception(
'Invalid boolean: expected 0 or 1, but got ${result}',
);
}
}

Unit deserializeUnit() {
Expand Down Expand Up @@ -146,4 +158,15 @@ abstract class BinaryDeserializer {
return number.toUnsigned(byteLength * 8);
}
}

void increaseContainerDepth() {
if (containerDepthBudget == 0) {
throw Exception('exceeded maximum container depth');
}
containerDepthBudget -= 1;
}

void decreaseContainerDepth() {
containerDepthBudget += 1;
}
}
22 changes: 20 additions & 2 deletions serde-generate/runtime/dart/serde/binary_serializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
part of serde;

abstract class BinarySerializer {
BinarySerializer({
required this.containerDepthBudget,
});

int containerDepthBudget;
final List<int> output = List<int>.empty(growable: true);

Uint8List get bytes {
Expand Down Expand Up @@ -129,12 +134,25 @@ abstract class BinarySerializer {
void serializeLength(int len);

void serializeInt128(Int128 value) {
serializeInt64(value.low.toInt());
serializeInt64(value.high.toInt());
serializeUint64(Uint64(value.low));
serializeUint64(Uint64(value.high));
}

void serializeUint128(Uint128 value) {
serializeUint64(Uint64(value.low));
serializeUint64(Uint64(value.high));
}

void increaseContainerDepth() {
if (containerDepthBudget == 0) {
throw Exception('exceeded maximum container depth');
}
containerDepthBudget -= 1;
}

void decreaseContainerDepth() {
containerDepthBudget += 1;
}

void sortMapEntries(List<int> offsets);
}
58 changes: 25 additions & 33 deletions serde-generate/src/dart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl<'a> CodeGenerator<'a> {
r#"name: {}

environment:
sdk: '>=2.14.0 <3.0.0'
sdk: '>=3.0.0 <4.0.0'

dependencies:
meta: ^1.0.0
Expand Down Expand Up @@ -670,53 +670,43 @@ return obj;
}

if self.generator.config.serialization {
// a struct (UnitStruct) with zero fields
if variant_index.is_none() && fields.is_empty() {
writeln!(
self.out,
"\n{}.deserialize(BinaryDeserializer deserializer);",
self.quote_qualified_name(name)
)?;
// Deserialize (struct) or Load (variant)
} else if variant_index.is_none() {
writeln!(
self.out,
"\n{}.deserialize(BinaryDeserializer deserializer) :",
self.quote_qualified_name(name)
)?;
} else if !fields.is_empty() {
if variant_index.is_none() {
writeln!(
self.out,
"\n{}.load(BinaryDeserializer deserializer) :",
"\nstatic {} deserialize(BinaryDeserializer deserializer) {{",
self.quote_qualified_name(name)
)?;
} else {
writeln!(
self.out,
"\n{}.load(BinaryDeserializer deserializer);",
"\nstatic {} load(BinaryDeserializer deserializer) {{",
self.quote_qualified_name(name)
)?;
}

self.out.indent();
for (index, field) in fields.iter().enumerate() {
if index == field_count - 1 {
writeln!(
self.out,
"{} = {};",
self.quote_field(&field.name.to_mixed_case()),
self.quote_deserialize(&field.value)
)?;
} else {
writeln!(
self.out,
"{} = {},",
self.quote_field(&field.name.to_mixed_case()),
self.quote_deserialize(&field.value)
)?;
}
writeln!(self.out, "deserializer.increaseContainerDepth();")?;
writeln!(
self.out,
"final instance = {}(",
self.quote_qualified_name(name)
)?;
self.out.indent();
for field in fields {
writeln!(
self.out,
"{}: {},",
self.quote_field(&field.name.to_mixed_case()),
self.quote_deserialize(&field.value)
)?;
}
self.out.unindent();
writeln!(self.out, ");")?;
writeln!(self.out, "deserializer.decreaseContainerDepth();")?;
writeln!(self.out, "return instance;")?;
self.out.unindent();
writeln!(self.out, "}}")?;

if variant_index.is_none() {
for encoding in &self.generator.config.encodings {
Expand Down Expand Up @@ -780,6 +770,7 @@ return obj;
if self.generator.config.serialization {
writeln!(self.out, "\nvoid serialize(BinarySerializer serializer) {{",)?;
self.out.indent();
writeln!(self.out, "serializer.increaseContainerDepth();")?;
if let Some(index) = variant_index {
writeln!(self.out, "serializer.serializeVariantIndex({});", index)?;
}
Expand All @@ -793,6 +784,7 @@ return obj;
)
)?;
}
writeln!(self.out, "serializer.decreaseContainerDepth();")?;
self.out.unindent();
writeln!(self.out, "}}")?;

Expand Down
Loading
Loading