Skip to content

Commit 48b3093

Browse files
Dynamic Int generation + integer primitive fixes (#97)
* Dynamic Int generation Previously the `Int` struct was statically defined. This causes issues because some traits, wrappers, etc are only needed for certain runtime configurations (e.g. preserve-encodings) or usages (e.g. if it's used as a key). This also introduces more functionality like a wasm wrapper again and conversion traits. Fixes #55 * Fixes for different int precisions + Tests Fixes #57 Also addresses issues with other primtive types e.g. i8, i64, u8, etc and adds unit tests to cover all of them. Fixes a few issues with wasm bindings for `Int`. * Misc fixes * Option<T> for non-primitive T compiles properly now (doesn't use a ref) * removed TODO that was actually already implemented * fix for maps in keys (OrderedHashMap now derives comparisons when needed) * Full nint support Now even with `preserve-encodings=false` we use the `*negative_integer_sz()` functions in `cbor_event` as the non-`_sz` ones have two issues: 1) they use `i64` as an interface and thus only cover half of `nint`'s range and 2) there is a panic when serializing `i64::MIN` so even for `i64` it is not complete. See primetype/cbor_event#9 This commit also changes the `nint` type to be an unsigned `u64` to cover the complete range and not allow in the rust code values from both sides of the sign. This might be a little awkward it is represents exactly the values possible with `nint`. Negative constants are also added by this. * link cbor_event upstream issue Co-authored-by: Sebastien Guillemot <[email protected]>
1 parent a354a5e commit 48b3093

File tree

11 files changed

+429
-113
lines changed

11 files changed

+429
-113
lines changed

src/generation.rs

Lines changed: 288 additions & 34 deletions
Large diffs are not rendered by default.

src/intermediate.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ pub enum Representation {
366366
pub enum FixedValue {
367367
Null,
368368
Bool(bool),
369-
Int(isize),
369+
Nint(isize),
370370
Uint(usize),
371371
// float not supported right now - doesn't appear to be in cbor_event
372372
//Float(f64),
@@ -386,7 +386,7 @@ impl FixedValue {
386386
true => "True",
387387
false => "False",
388388
}),
389-
FixedValue::Int(i) => VariantIdent::new_custom(format!("U{}", i)),
389+
FixedValue::Nint(i) => VariantIdent::new_custom(format!("U{}", i)),
390390
FixedValue::Uint(u) => VariantIdent::new_custom(format!("I{}", u)),
391391
//FixedValue::Float(f) => format!("F{}", f),
392392
FixedValue::Text(s) => VariantIdent::new_custom(convert_to_alphanumeric(&convert_to_camel_case(&s))),
@@ -398,7 +398,7 @@ impl FixedValue {
398398
match self {
399399
FixedValue::Null => buf.write_special(cbor_event::Special::Null),
400400
FixedValue::Bool(b) => buf.write_special(cbor_event::Special::Bool(*b)),
401-
FixedValue::Int(i) => buf.write_negative_integer(*i as i64),
401+
FixedValue::Nint(i) => buf.write_negative_integer(*i as i64),
402402
FixedValue::Uint(u) => buf.write_unsigned_integer(*u as u64),
403403
FixedValue::Text(s) => buf.write_text(s),
404404
}.expect("Unable to serialize key for canonical ordering");
@@ -444,8 +444,7 @@ impl Primitive {
444444
Primitive::I32 => "i32",
445445
Primitive::U64 => "u64",
446446
Primitive::I64 => "i64",
447-
// TODO: this should ideally be a u64 but the cbor_event lib takes i64 anyway so we don't get that extra precision
448-
Primitive::N64 => "i64",
447+
Primitive::N64 => "u64",
449448
Primitive::Str => "String",
450449
Primitive::Bytes => "Vec<u8>",
451450
})
@@ -672,11 +671,10 @@ impl RustType {
672671
Primitive::I16 |
673672
Primitive::U16 |
674673
Primitive::I32 |
675-
Primitive::U32 => true,
676-
// since we generate these as BigNum/Int wrappers we can't nest them
674+
Primitive::U32 |
677675
Primitive::I64 |
678676
Primitive::N64 |
679-
Primitive::U64 => false,
677+
Primitive::U64 => true,
680678
// Bytes is already implemented as Vec<u8> so we can't nest it
681679
Primitive::Bytes => false,
682680
// Vec<String> is not supported by wasm-bindgen
@@ -743,25 +741,34 @@ impl RustType {
743741

744742
/// Function parameter TYPE from wasm (i.e. ref for non-primitives, value for supported primitives)
745743
pub fn for_wasm_param(&self) -> String {
744+
self.for_wasm_param_impl(false)
745+
}
746+
747+
fn for_wasm_param_impl(&self, force_not_ref: bool) -> String {
748+
let opt_ref = if force_not_ref {
749+
""
750+
} else {
751+
"&"
752+
};
746753
match self {
747754
RustType::Fixed(_) => panic!("should not expose Fixed type to wasm, only here for serialization: {:?}", self),
748755
RustType::Primitive(p) => p.to_string(),
749-
RustType::Rust(ident) => format!("&{}", ident),
756+
RustType::Rust(ident) => format!("{}{}", opt_ref, ident),
750757
RustType::Array(ty) => if self.directly_wasm_exposable() {
751758
ty.name_as_wasm_array()
752759
} else {
753-
format!("&{}", ty.name_as_wasm_array())
760+
format!("{}{}", opt_ref, ty.name_as_wasm_array())
754761
},
755-
RustType::Tagged(_tag, ty) => ty.for_wasm_param(),
756-
RustType::Optional(ty) => format!("Option<{}>", ty.for_wasm_param()),
757-
RustType::Map(_k, _v) => format!("&{}", self.for_wasm_member()),
762+
RustType::Tagged(_tag, ty) => ty.for_wasm_param_impl(force_not_ref),
763+
RustType::Optional(ty) => format!("Option<{}>", ty.for_wasm_param_impl(true)),
764+
RustType::Map(_k, _v) => format!("{}{}", opt_ref, self.for_wasm_member()),
758765
// it might not be worth generating this as alises are ignored by wasm-pack build, but
759766
// that could change in the future so as long as it doens't cause issues we'll leave it
760767
RustType::Alias(ident, ty) => match &**ty {
761-
RustType::Rust(_) => format!("&{}", ident),
768+
RustType::Rust(_) => format!("{}{}", opt_ref, ident),
762769
_ => ident.to_string(),
763770
}
764-
RustType::CBORBytes(ty) => ty.for_wasm_member(),
771+
RustType::CBORBytes(ty) => ty.for_wasm_param(),
765772
}
766773
}
767774

@@ -1005,7 +1012,7 @@ impl RustType {
10051012
match self {
10061013
RustType::Fixed(f) => vec![match f {
10071014
FixedValue::Uint(_) => CBORType::UnsignedInteger,
1008-
FixedValue::Int(_) => CBORType::NegativeInteger,
1015+
FixedValue::Nint(_) => CBORType::NegativeInteger,
10091016
FixedValue::Text(_) => CBORType::Text,
10101017
FixedValue::Null => CBORType::Special,
10111018
FixedValue::Bool(_) => CBORType::Special,

src/main.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
101101
gen_scope.wasm().import("wasm_bindgen::prelude", "*");
102102
gen_scope
103103
.wasm()
104-
.raw("mod prelude;")
105104
.raw("use std::collections::BTreeMap;");
106105

107106
if CLI_ARGS.preserve_encodings {
@@ -193,7 +192,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
193192
wasm_toml.push_str("serde_json = \"1.0.57\"\n");
194193
}
195194
std::fs::write(rust_dir.join("wasm/Cargo.toml"), wasm_toml)?;
196-
std::fs::copy("static/prelude_wasm.rs", rust_dir.join("wasm/src/prelude.rs"))?;
197195
}
198196

199197
// json-gen crate for exporting JSON schemas

src/parsing.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ fn parse_type(types: &mut IntermediateTypes, type_name: &RustIdent, type_choice:
353353
},
354354
// Note: bool constants are handled via Type2::Typename
355355
Type2::IntValue{ value, .. } => {
356-
let fallback_type = RustType::Fixed(FixedValue::Int(*value));
356+
let fallback_type = RustType::Fixed(FixedValue::Nint(*value));
357357

358358
let control = type1.operator.as_ref().map(|op| parse_control_operator(types, &Type2AndParent { parent: type1, type2: &type1.type2 }, op));
359359
let base_type = match control {
@@ -588,7 +588,7 @@ fn rust_type_from_type2(types: &mut IntermediateTypes, type2: &Type2AndParent) -
588588
// TODO: socket plugs (used in hash type)
589589
match &type2.type2 {
590590
Type2::UintValue{ value, .. } => RustType::Fixed(FixedValue::Uint(*value)),
591-
Type2::IntValue{ value, .. } => RustType::Fixed(FixedValue::Int(*value)),
591+
Type2::IntValue{ value, .. } => RustType::Fixed(FixedValue::Nint(*value)),
592592
//Type2::FloatValue{ value, .. } => RustType::Fixed(FixedValue::Float(*value)),
593593
Type2::TextValue{ value, .. } => RustType::Fixed(FixedValue::Text(value.to_string())),
594594
Type2::Typename{ ident, generic_args, .. } => {
@@ -751,14 +751,14 @@ fn group_entry_to_key(entry: &GroupEntry) -> Option<FixedValue> {
751751
match ge.member_key.as_ref()? {
752752
MemberKey::Value{ value, .. } => match value {
753753
cddl::token::Value::UINT(x) => Some(FixedValue::Uint(*x)),
754-
cddl::token::Value::INT(x) => Some(FixedValue::Int(*x)),
754+
cddl::token::Value::INT(x) => Some(FixedValue::Nint(*x)),
755755
cddl::token::Value::TEXT(x) => Some(FixedValue::Text(x.to_string())),
756756
_ => panic!("unsupported map identifier(1): {:?}", value),
757757
},
758758
MemberKey::Bareword{ ident, .. } => Some(FixedValue::Text(ident.to_string())),
759759
MemberKey::Type1{ t1, .. } => match &t1.type2 {
760760
Type2::UintValue{ value, .. } => Some(FixedValue::Uint(*value)),
761-
Type2::IntValue{ value, .. } => Some(FixedValue::Int(*value)),
761+
Type2::IntValue{ value, .. } => Some(FixedValue::Nint(*value)),
762762
Type2::TextValue{ value, .. } => Some(FixedValue::Text(value.to_string())),
763763
_ => panic!("unsupported map identifier(2): {:?}", entry),
764764
},

static/ordered_hash_map.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use core::hash::Hash;
1+
use core::hash::{Hash, Hasher};
22

3-
#[derive(Clone, Debug)]
3+
#[derive(Clone, Debug, Ord, Eq, PartialEq, PartialOrd)]
44
pub struct OrderedHashMap<K, V>(linked_hash_map::LinkedHashMap<K, V>) where
55
K : Hash + Eq + Ord;
66

@@ -24,3 +24,9 @@ impl<K, V> OrderedHashMap<K, V> where K : Hash + Eq + Ord {
2424
}
2525
}
2626

27+
impl<K, V> Hash for OrderedHashMap<K, V> where K : Hash + Eq + Ord, V : Hash {
28+
fn hash<H: Hasher>(&self, h: &mut H) {
29+
self.0.hash(h);
30+
}
31+
}
32+

static/prelude.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -144,29 +144,3 @@ impl<T: Deserialize + Sized> FromBytes for T {
144144
}
145145
}
146146

147-
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
148-
pub enum Int {
149-
Uint(u64),
150-
Nint(u64),
151-
}
152-
153-
impl cbor_event::se::Serialize for Int {
154-
fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer<W>) -> cbor_event::Result<&'se mut Serializer<W>> {
155-
match self {
156-
Self::Uint(x) => serializer.write_unsigned_integer(*x),
157-
Self::Nint(x) => serializer.write_negative_integer(-(*x as i128) as i64),
158-
}
159-
}
160-
}
161-
162-
impl Deserialize for Int {
163-
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
164-
(|| -> Result<_, DeserializeError> {
165-
match raw.cbor_type()? {
166-
cbor_event::Type::UnsignedInteger => Ok(Self::Uint(raw.unsigned_integer()?)),
167-
cbor_event::Type::NegativeInteger => Ok(Self::Nint(-raw.negative_integer()? as u64)),
168-
_ => Err(DeserializeFailure::NoVariantMatched.into()),
169-
}
170-
})().map_err(|e| e.annotate("Int"))
171-
}
172-
}

static/prelude_wasm.rs

Lines changed: 0 additions & 28 deletions
This file was deleted.

tests/core/input.cddl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,23 @@ u16 = uint .le 65535
4242
u32 = 0..4294967295
4343
u64 = uint .size 8 ; 8 bytes
4444
i8 = -128..127
45-
i64 = int .size 8 ; 8 bytes
45+
i64 = int .size 8 ; 8 bytes
46+
47+
signed_ints = [
48+
u_8: uint .size 1,
49+
u_16: uint .size 2,
50+
u_32: uint .size 4,
51+
u_64: uint .size 8,
52+
i_8: int .size 1,
53+
i_16: int .size 2,
54+
i_32: int .size 4,
55+
i_64: int .size 8,
56+
n_64: nint
57+
u64_max: 18446744073709551615,
58+
; this test assumes i64::BITS == isize::BITS (i.e. 64-bit programs) or else the cddl parsing lib would mess up
59+
; if this test fails on your platform we might need to either remove this part
60+
; or make a fix for the cddl library.
61+
; The fix would be ideal as even though the true min in CBOR would be -u64::MAX
62+
; we can't test that since isize::BITS is never > 64 in any normal system and likely never will be
63+
i64_min: -9223372036854775808
64+
]

tests/core/tests.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,12 @@ mod tests {
118118
assert_eq!(0u64, U64::from(0u64));
119119
assert_eq!(0i64, I64::from(0i64));
120120
}
121+
122+
#[test]
123+
fn signed_ints() {
124+
let min = SignedInts::new(u8::MIN, u16::MIN, u32::MIN, u64::MIN, i8::MIN, i16::MIN, i32::MIN, i64::MIN, u64::MIN);
125+
deser_test(&min);
126+
let max = SignedInts::new(u8::MAX, u16::MAX, u32::MAX, u64::MAX, i8::MAX, i16::MAX, i32::MAX, i64::MAX, u64::MAX);
127+
deser_test(&max);
128+
}
121129
}

tests/preserve-encodings/input.cddl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,22 @@ foo_bytes = bytes .cbor foo
3838

3939
; since we don't generate code for definitions like the above (should we if no one refers to it?)
4040
cbor_in_cbor = [foo_bytes, uint_bytes: bytes .cbor uint]
41+
42+
signed_ints = [
43+
u_8: uint .size 1,
44+
u_16: uint .size 2,
45+
u_32: uint .size 4,
46+
u_64: uint .size 8,
47+
i_8: int .size 1,
48+
i_16: int .size 2,
49+
i_32: int .size 4,
50+
i_64: int .size 8,
51+
n_64: nint
52+
u64_max: 18446744073709551615,
53+
; this test assumes i64::BITS == isize::BITS or else the cddl parsing lib would mess up
54+
; if this test fails on your platform we might need to either remove this part
55+
; or make a fix for the cddl library.
56+
; The fix would be ideal as even though the true min in CBOR would be -u64::MAX
57+
; we can't support since isize::BITS is never > 64 in any normal system and likely never will be
58+
i64_min: -9223372036854775808
59+
]

0 commit comments

Comments
 (0)