From a1bc244d8136ee1e39d99cf3666839618dd845a6 Mon Sep 17 00:00:00 2001 From: Michael Ward Date: Mon, 30 Dec 2024 06:38:32 -0600 Subject: [PATCH] [Rust] encoding primitive arrays now supports slice instead of array (issue #1021) (#1040) * [Rust] codegen now implements: "From<&'a mut WriteBuf<'a>> for &'a mut [u8]" * [Rust] refactored codegen of primitive array encoding to accept slice instead of array * [Rust] updated some tests * [Rust] updated codegen to create additional function for primitive arrays that will accept an "impl Iterator". This allows an arbitrary number of items to be passed in, which will guarantee the correct number of items are encoded (defined 'nullValue' will be used where appropriate) * [Rust] generate at_most_*_items_from_slice setter for fixed sized primitive array in message * [Rust] add padded support for slice setter for primitive array field * [Rust] simplify impl of generated slice aware methods * merging in PR * [Rust] updated generator to create "_from_iter" and "_zero_padded" functions for primitive array * [Rust] updated generator to create "_from_iter" and "_zero_padded" functions for primitive array * fixed formatting issues * removed unused format argument --------- Co-authored-by: Michael Ward Co-authored-by: Elvis Wang --- build.gradle | 1 + rust/Cargo.toml | 1 + rust/tests/baseline_enum_from_str.rs | 21 +- rust/tests/extension_test.rs | 4 +- rust/tests/fixed_sized_primitive_array.rs | 1110 +++++++++++++++++ .../sbe/generation/rust/LibRsDef.java | 7 + .../sbe/generation/rust/RustGenerator.java | 218 +++- .../sbe/generation/rust/RustUtil.java | 6 + .../sbe/generation/rust/RustUtilTest.java | 3 +- .../fixed-sized-primitive-array-types.xml | 73 ++ 10 files changed, 1369 insertions(+), 75 deletions(-) create mode 100644 rust/tests/fixed_sized_primitive_array.rs create mode 100644 sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml diff --git a/build.gradle b/build.gradle index f4c65045ba..3e3b3b2ee7 100644 --- a/build.gradle +++ b/build.gradle @@ -665,6 +665,7 @@ tasks.register('generateRustTestCodecs', JavaExec) { 'sbe-tool/src/test/resources/issue984.xml', 'sbe-tool/src/test/resources/issue987.xml', 'sbe-tool/src/test/resources/issue1028.xml', + 'sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml', 'sbe-tool/src/test/resources/example-bigendian-test-schema.xml', 'sbe-tool/src/test/resources/nested-composite-name.xml', ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f15d4d2f6d..2d4f3d4a49 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -19,6 +19,7 @@ issue_987 = { path = "../generated/rust/issue987" } issue_1028 = { path = "../generated/rust/issue1028" } baseline_bigendian = { path = "../generated/rust/baseline-bigendian" } nested_composite_name = { path = "../generated/rust/nested-composite-name" } +fixed_sized_primitive_array = { path = "../generated/rust/fixed_sized_primitive_array" } [dev-dependencies] criterion = "0.5" diff --git a/rust/tests/baseline_enum_from_str.rs b/rust/tests/baseline_enum_from_str.rs index 4e52fd4bf9..a111d28364 100644 --- a/rust/tests/baseline_enum_from_str.rs +++ b/rust/tests/baseline_enum_from_str.rs @@ -1,21 +1,22 @@ +use BoostType::{NullVal, KERS, NITROUS, SUPERCHARGER, TURBO}; use examples_baseline::{ boost_type::BoostType, }; #[test] fn test_boost_type_from_str() -> Result<(), ()> { - assert_eq!("TURBO".parse::()?, BoostType::TURBO, "Parse \"TURBO\" as BoostType"); - assert_eq!("SUPERCHARGER".parse::()?, BoostType::SUPERCHARGER, "Parse \"SUPERCHARGER\" as BoostType"); - assert_eq!("NITROUS".parse::()?, BoostType::NITROUS, "Parse \"NITROUS\" as BoostType"); - assert_eq!("KERS".parse::()?, BoostType::KERS, "Parse \"KERS\" as BoostType"); + assert_eq!("TURBO".parse::()?, TURBO, "Parse \"TURBO\" as BoostType"); + assert_eq!("SUPERCHARGER".parse::()?, SUPERCHARGER, "Parse \"SUPERCHARGER\" as BoostType"); + assert_eq!("NITROUS".parse::()?, NITROUS, "Parse \"NITROUS\" as BoostType"); + assert_eq!("KERS".parse::()?, KERS, "Parse \"KERS\" as BoostType"); - assert_eq!("Turbo".parse::()?, BoostType::NullVal, "Parse \"Turbo\" as BoostType"); - assert_eq!("Supercharger".parse::()?, BoostType::NullVal, "Parse \"Supercharger\" as BoostType"); - assert_eq!("Nitrous".parse::()?, BoostType::NullVal, "Parse \"Nitrous\" as BoostType"); - assert_eq!("Kers".parse::()?, BoostType::NullVal, "Parse \"Kers\" as BoostType"); + assert_eq!("Turbo".parse::()?, NullVal, "Parse \"Turbo\" as BoostType"); + assert_eq!("Supercharger".parse::()?, NullVal, "Parse \"Supercharger\" as BoostType"); + assert_eq!("Nitrous".parse::()?, NullVal, "Parse \"Nitrous\" as BoostType"); + assert_eq!("Kers".parse::()?, NullVal, "Parse \"Kers\" as BoostType"); - assert_eq!("AA".parse::()?, BoostType::NullVal, "Parse \"AA\" as BoostType"); - assert_eq!("".parse::().unwrap(), BoostType::NullVal, "Parse \"\" as BoostType"); + assert_eq!("AA".parse::()?, NullVal, "Parse \"AA\" as BoostType"); + assert_eq!("".parse::().unwrap(), NullVal, "Parse \"\" as BoostType"); Ok(()) } diff --git a/rust/tests/extension_test.rs b/rust/tests/extension_test.rs index d7258bc39b..ab62a7715c 100644 --- a/rust/tests/extension_test.rs +++ b/rust/tests/extension_test.rs @@ -187,7 +187,7 @@ fn encode_car_from_scratch() -> SbeResult<(usize, Vec)> { car.available(BooleanType::T); car.code(Model::A); car.some_numbers(&[0, 1, 2, 3]); - car.vehicle_code(&[97, 98, 99, 100, 101, 102]); // abcdef + car.vehicle_code_from_iter(b"abcdef_extra_is_ignored".into_iter().copied()); // abcdef extras.set_cruise_control(true); extras.set_sports_pack(true); @@ -197,7 +197,7 @@ fn encode_car_from_scratch() -> SbeResult<(usize, Vec)> { let mut engine = car.engine_encoder(); engine.capacity(2000); engine.num_cylinders(4); - engine.manufacturer_code(&[49, 50, 51]); // 123 + engine.manufacturer_code(b"123"); engine.efficiency(35); engine.booster_enabled(BooleanType::T); let mut booster = engine.booster_encoder(); diff --git a/rust/tests/fixed_sized_primitive_array.rs b/rust/tests/fixed_sized_primitive_array.rs new file mode 100644 index 0000000000..8542e3d488 --- /dev/null +++ b/rust/tests/fixed_sized_primitive_array.rs @@ -0,0 +1,1110 @@ +use fixed_sized_primitive_array::{ + demo_codec::{DemoDecoder, DemoEncoder}, + message_header_codec::{MessageHeaderDecoder, ENCODED_LENGTH}, + ReadBuf, WriteBuf, +}; + +fn create_encoder(buffer: &mut [u8]) -> DemoEncoder { + let encoder = DemoEncoder::default().wrap(WriteBuf::new(buffer), ENCODED_LENGTH); + let mut header = encoder.header(0); + header.parent().unwrap() +} + +#[test] +fn test_encode_then_decode_u8_slice() { + let uninit = 1u8; + + let test_data = [ + b"" as &[u8], + b"0" as &[u8], + b"01" as &[u8], + b"012" as &[u8], + b"0123" as &[u8], + b"01234" as &[u8], + b"012345" as &[u8], + b"0123456" as &[u8], + b"01234567" as &[u8], + b"012345678" as &[u8], + b"0123456789" as &[u8], + b"0123456789A" as &[u8], + b"0123456789AB" as &[u8], + b"0123456789ABC" as &[u8], + b"0123456789ABCD" as &[u8], + b"0123456789ABCDE" as &[u8], + b"0123456789ABCDEF" as &[u8], + b"0123456789abcdef" as &[u8], + b"0123456789abcdef0" as &[u8], + b"0123456789abcdef01" as &[u8], + b"0123456789abcdef012" as &[u8], + b"0123456789abcdef0123" as &[u8], + b"0123456789abcdef01234" as &[u8], + b"0123456789abcdef012345" as &[u8], + b"0123456789abcdef0123456" as &[u8], + b"0123456789abcdef01234567" as &[u8], + b"0123456789abcdef012345678" as &[u8], + b"0123456789abcdef0123456789" as &[u8], + b"0123456789abcdef0123456789A" as &[u8], + b"0123456789abcdef0123456789AB" as &[u8], + b"0123456789abcdef0123456789ABC" as &[u8], + b"0123456789abcdef0123456789ABCD" as &[u8], + b"0123456789abcdef0123456789ABCDE" as &[u8], + b"0123456789abcdef0123456789ABCDEF" as &[u8], + ]; + + // + // + // + // + // + // + // + // + macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { + ($encode_func:expr, $decode_func:expr, $end_encode_func:expr, $end_decode_func:expr) => { + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let end_encode_func = $end_encode_func; + let end_decode_func = $end_decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = [uninit; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice.into_iter().copied()); + + let end = 1i32; + end_encode_func(&mut encoder, end); + + // decode... + let buf = ReadBuf::new(buffer.as_slice()); + let header = MessageHeaderDecoder::default().wrap(buf, 0); + + let decoder = DemoDecoder::default().header(header, 0); + let decoded = decode_func(&decoder); + for each_idx in 0..effective_len { + assert_eq!( + each_slice[each_idx], decoded[each_idx], + "Item mismatched at {}/{}", + each_idx, cur_len + ); + } + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], uninit, + "Item should not be padded ZERO at {}/{}", + each_idx, cur_len + ); + } + let decoded_end = end_decode_func(&decoder); + assert_eq!(decoded_end, end, "End Item should equal",); + } + }; + } + + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_char_from_iter, + DemoDecoder::fixed_16_char, + DemoEncoder::fixed_16_char_end, + DemoDecoder::fixed_16_char_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_char_from_iter, + DemoDecoder::fixed_16_ascii_char, + DemoEncoder::fixed_16_ascii_char_end, + DemoDecoder::fixed_16_ascii_char_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_char_from_iter, + DemoDecoder::fixed_16_gb_18030_char, + DemoEncoder::fixed_16_gb_18030_char_end, + DemoDecoder::fixed_16_gb_18030_char_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_char_from_iter, + DemoDecoder::fixed_16_utf_8_char, + DemoEncoder::fixed_16_utf_8_char_end, + DemoDecoder::fixed_16_utf_8_char_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_u8_from_iter, + DemoDecoder::fixed_16_u8, + DemoEncoder::fixed_16_u8_end, + DemoDecoder::fixed_16_u8_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_u8_from_iter, + DemoDecoder::fixed_16_ascii_u8, + DemoEncoder::fixed_16_ascii_u8_end, + DemoDecoder::fixed_16_ascii_u8_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_u8_from_iter, + DemoDecoder::fixed_16_gb_18030_u8, + DemoEncoder::fixed_16_gb_18030_u8_end, + DemoDecoder::fixed_16_gb_18030_u8_end + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_u8_from_iter, + DemoDecoder::fixed_16_utf_8_u8, + DemoEncoder::fixed_16_utf_8u_8_end, + DemoDecoder::fixed_16_utf_8u_8_end + ); +} + +#[test] +fn test_encode_then_decode_non_u8_signed_primitive_slice() { + let uninit = 1u8; + + // + // + // + // + macro_rules! run_encode_then_decode_for_array_of_signed_len_16 { + ($encode_func:expr, $decode_func:expr, $end_encode_func:expr, $end_decode_func:expr, $i_type:ty, $existed:expr) => { + let test_data = [ + &[] as &[$i_type], + &[1 as $i_type] as &[$i_type], + &[0 as $i_type] as &[$i_type], + &[-1 as $i_type] as &[$i_type], + &[-1, 1 as $i_type] as &[$i_type], + &[-1, 0, 1 as $i_type] as &[$i_type], + &[-2, -1, 1, 2 as $i_type] as &[$i_type], + &[-2, -1, 0, 1, 2 as $i_type] as &[$i_type], + &[-3, -2, -1, 1, 2, 3 as $i_type] as &[$i_type], + &[-3, -2, -1, 0, 1, 2, 3 as $i_type] as &[$i_type], + &[-4, -3, -2, -1, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[-4, -3, -2, -1, 0, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[-5, -4, -3, -2, -1, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[-6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[-7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7 as $i_type] as &[$i_type], + &[ + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 as $i_type, + ] as &[$i_type], + &[ + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + &[ + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + ]; + + let existed = $existed; + + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let end_encode_func = $end_encode_func; + let end_decode_func = $end_decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = [uninit; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice.into_iter().copied()); + + let end = 1i32; + end_encode_func(&mut encoder, end); + + // decode... + let buf = ReadBuf::new(buffer.as_slice()); + let header = MessageHeaderDecoder::default().wrap(buf, 0); + + let decoder = DemoDecoder::default().header(header, 0); + let decoded = decode_func(&decoder); + for each_idx in 0..effective_len { + assert_eq!( + each_slice[each_idx], + decoded[each_idx], + "Item mismatched at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], + existed, + "Item should not be padded ZERO at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + let decoded_end = end_decode_func(&decoder); + assert_eq!(decoded_end, end, "End Item should equal",); + } + }; + } + + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i8_from_iter, + DemoDecoder::fixed_16_i8, + DemoEncoder::fixed_16_i8_end, + DemoDecoder::fixed_16_i8_end, + i8, i8::from_le_bytes([uninit]) + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i16_from_iter, + DemoDecoder::fixed_16_i16, + DemoEncoder::fixed_16_i16_end, + DemoDecoder::fixed_16_i16_end, + i16, i16::from_le_bytes([uninit, uninit]) + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i32_from_iter, + DemoDecoder::fixed_16_i32, + DemoEncoder::fixed_16_i32_end, + DemoDecoder::fixed_16_i32_end, + i32, i32::from_le_bytes([uninit, uninit, uninit, uninit]) + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i64_from_iter, + DemoDecoder::fixed_16_i64, + DemoEncoder::fixed_16_i64_end, + DemoDecoder::fixed_16_i64_end, + i64, i64::from_le_bytes([uninit, uninit, uninit, uninit, uninit, uninit, uninit, uninit]) + ); +} + +#[test] +fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { + let uninit = 1u8; + + // + // + macro_rules! run_encode_then_decode_for_array_of_unsigned_len_16 { + ($encode_func:expr, $decode_func:expr, $end_encode_func:expr, $end_decode_func:expr, $i_type:ty, $existed:expr) => { + let test_data = [ + &[] as &[$i_type], + &[1 as $i_type] as &[$i_type], + &[0 as $i_type] as &[$i_type], + &[11 as $i_type] as &[$i_type], + &[11, 1 as $i_type] as &[$i_type], + &[11, 0, 1 as $i_type] as &[$i_type], + &[12, 11, 1, 2 as $i_type] as &[$i_type], + &[12, 11, 0, 1, 2 as $i_type] as &[$i_type], + &[13, 12, 11, 1, 2, 3 as $i_type] as &[$i_type], + &[13, 12, 11, 0, 1, 2, 3 as $i_type] as &[$i_type], + &[14, 13, 12, 11, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[14, 13, 12, 11, 0, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[15, 14, 13, 12, 11, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[15, 14, 13, 12, 11, 0, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[16, 15, 14, 13, 12, 11, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[16, 15, 14, 13, 12, 11, 0, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[17, 16, 15, 14, 13, 12, 11, 1, 2, 3, 4, 5, 6, 7 as $i_type] as &[$i_type], + &[ + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 as $i_type, + ] as &[$i_type], + &[ + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + &[ + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + ]; + + let existed = $existed; + + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let end_encode_func = $end_encode_func; + let end_decode_func = $end_decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = [uninit; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice.into_iter().copied()); + + let end = 1i32; + end_encode_func(&mut encoder, end); + + // decode... + let buf = ReadBuf::new(buffer.as_slice()); + let header = MessageHeaderDecoder::default().wrap(buf, 0); + + let decoder = DemoDecoder::default().header(header, 0); + let decoded = decode_func(&decoder); + for each_idx in 0..effective_len { + assert_eq!( + each_slice[each_idx], + decoded[each_idx], + "Item mismatched at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], + existed, + "Item should not be padded ZERO at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + let decoded_end = end_decode_func(&decoder); + assert_eq!(decoded_end, end, "End Item should equal",); + } + }; + } + + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u16_from_iter, + DemoDecoder::fixed_16_u16, + DemoEncoder::fixed_16_u16_end, + DemoDecoder::fixed_16_u16_end, + u16, + u16::from_le_bytes([uninit, uninit]) + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u32_from_iter, + DemoDecoder::fixed_16_u32, + DemoEncoder::fixed_16_u32_end, + DemoDecoder::fixed_16_u32_end, + u32, + u32::from_le_bytes([uninit, uninit, uninit, uninit]) + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u64_from_iter, + DemoDecoder::fixed_16_u64, + DemoEncoder::fixed_16_u64_end, + DemoDecoder::fixed_16_u64_end, + u64, + u64::from_le_bytes([uninit, uninit, uninit, uninit, uninit, uninit, uninit, uninit]) + ); +} + +#[test] +fn test_encode_then_decode_u8_slice_padded() { + let uninit = 1u8; + + let test_data = [ + b"" as &[u8], + b"0" as &[u8], + b"01" as &[u8], + b"012" as &[u8], + b"0123" as &[u8], + b"01234" as &[u8], + b"012345" as &[u8], + b"0123456" as &[u8], + b"01234567" as &[u8], + b"012345678" as &[u8], + b"0123456789" as &[u8], + b"0123456789A" as &[u8], + b"0123456789AB" as &[u8], + b"0123456789ABC" as &[u8], + b"0123456789ABCD" as &[u8], + b"0123456789ABCDE" as &[u8], + b"0123456789ABCDEF" as &[u8], + b"0123456789abcdef" as &[u8], + b"0123456789abcdef0" as &[u8], + b"0123456789abcdef01" as &[u8], + b"0123456789abcdef012" as &[u8], + b"0123456789abcdef0123" as &[u8], + b"0123456789abcdef01234" as &[u8], + b"0123456789abcdef012345" as &[u8], + b"0123456789abcdef0123456" as &[u8], + b"0123456789abcdef01234567" as &[u8], + b"0123456789abcdef012345678" as &[u8], + b"0123456789abcdef0123456789" as &[u8], + b"0123456789abcdef0123456789A" as &[u8], + b"0123456789abcdef0123456789AB" as &[u8], + b"0123456789abcdef0123456789ABC" as &[u8], + b"0123456789abcdef0123456789ABCD" as &[u8], + b"0123456789abcdef0123456789ABCDE" as &[u8], + b"0123456789abcdef0123456789ABCDEF" as &[u8], + ]; + + // + // + // + // + // + // + // + // + macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { + ($encode_func:expr, $decode_func:expr, $end_encode_func:expr, $end_decode_func:expr,) => { + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let end_encode_func = $end_encode_func; + let end_decode_func = $end_decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = [uninit; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice); + + let end = 1i32; + end_encode_func(&mut encoder, end); + + // decode... + let buf = ReadBuf::new(buffer.as_slice()); + let header = MessageHeaderDecoder::default().wrap(buf, 0); + + let decoder = DemoDecoder::default().header(header, 0); + let decoded = decode_func(&decoder); + for each_idx in 0..effective_len { + assert_eq!( + each_slice[each_idx], decoded[each_idx], + "Item mismatched at {}/{}", + each_idx, cur_len + ); + } + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], 0, + "Item should be padded ZERO at {}/{}", + each_idx, cur_len + ); + } + let decoded_end = end_decode_func(&decoder); + assert_eq!(decoded_end, end, "End Item should equal",); + } + }; + } + + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_char_zero_padded, + DemoDecoder::fixed_16_char, + DemoEncoder::fixed_16_char_end, + DemoDecoder::fixed_16_char_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_char_zero_padded, + DemoDecoder::fixed_16_ascii_char, + DemoEncoder::fixed_16_ascii_char_end, + DemoDecoder::fixed_16_ascii_char_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_char_zero_padded, + DemoDecoder::fixed_16_gb_18030_char, + DemoEncoder::fixed_16_gb_18030_char_end, + DemoDecoder::fixed_16_gb_18030_char_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_char_zero_padded, + DemoDecoder::fixed_16_utf_8_char, + DemoEncoder::fixed_16_utf_8_char_end, + DemoDecoder::fixed_16_utf_8_char_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_u8_zero_padded, + DemoDecoder::fixed_16_u8, + DemoEncoder::fixed_16_u8_end, + DemoDecoder::fixed_16_u8_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_u8_zero_padded, + DemoDecoder::fixed_16_ascii_u8, + DemoEncoder::fixed_16_ascii_u8_end, + DemoDecoder::fixed_16_ascii_u8_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_u8_zero_padded, + DemoDecoder::fixed_16_gb_18030_u8, + DemoEncoder::fixed_16_gb_18030_u8_end, + DemoDecoder::fixed_16_gb_18030_u8_end, + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_u8_zero_padded, + DemoDecoder::fixed_16_utf_8_u8, + DemoEncoder::fixed_16_utf_8u_8_end, + DemoDecoder::fixed_16_utf_8u_8_end, + ); +} + +#[test] +fn test_encode_then_decode_non_u8_signed_primitive_slice_padded() { + let uninit = 1u8; + + // + // + // + // + macro_rules! run_encode_then_decode_for_array_of_signed_len_16 { + ($encode_func:expr, $decode_func:expr, $end_encode_func:expr, $end_decode_func:expr, $i_type:ty,) => { + let test_data = [ + &[] as &[$i_type], + &[1 as $i_type] as &[$i_type], + &[0 as $i_type] as &[$i_type], + &[-1 as $i_type] as &[$i_type], + &[-1, 1 as $i_type] as &[$i_type], + &[-1, 0, 1 as $i_type] as &[$i_type], + &[-2, -1, 1, 2 as $i_type] as &[$i_type], + &[-2, -1, 0, 1, 2 as $i_type] as &[$i_type], + &[-3, -2, -1, 1, 2, 3 as $i_type] as &[$i_type], + &[-3, -2, -1, 0, 1, 2, 3 as $i_type] as &[$i_type], + &[-4, -3, -2, -1, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[-4, -3, -2, -1, 0, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[-5, -4, -3, -2, -1, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[-6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[-7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7 as $i_type] as &[$i_type], + &[ + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 as $i_type, + ] as &[$i_type], + &[ + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + &[ + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + ]; + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let end_encode_func = $end_encode_func; + let end_decode_func = $end_decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = [uninit; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice); + + let end = 1i32; + end_encode_func(&mut encoder, end); + + // decode... + let buf = ReadBuf::new(buffer.as_slice()); + let header = MessageHeaderDecoder::default().wrap(buf, 0); + + let decoder = DemoDecoder::default().header(header, 0); + let decoded = decode_func(&decoder); + for each_idx in 0..effective_len { + assert_eq!( + each_slice[each_idx], + decoded[each_idx], + "Item mismatched at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], + 0, + "Item should be padded ZERO at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + let decoded_end = end_decode_func(&decoder); + assert_eq!(decoded_end, end, "End Item should equal",); + } + }; + } + + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i8_zero_padded, + DemoDecoder::fixed_16_i8, + DemoEncoder::fixed_16_i8_end, + DemoDecoder::fixed_16_i8_end, + i8, + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i16_zero_padded, + DemoDecoder::fixed_16_i16, + DemoEncoder::fixed_16_i16_end, + DemoDecoder::fixed_16_i16_end, + i16, + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i32_zero_padded, + DemoDecoder::fixed_16_i32, + DemoEncoder::fixed_16_i32_end, + DemoDecoder::fixed_16_i32_end, + i32, + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i64_zero_padded, + DemoDecoder::fixed_16_i64, + DemoEncoder::fixed_16_i64_end, + DemoDecoder::fixed_16_i64_end, + i64, + ); +} + +#[test] +fn test_encode_then_decode_non_u8_unsigned_primitive_slice_padded() { + let uninit = 1u8; + + // + // + // + macro_rules! run_encode_then_decode_for_array_of_unsigned_len_16 { + ($encode_func:expr, $decode_func:expr, $end_encode_func:expr, $end_decode_func:expr, $i_type:ty) => { + let test_data = [ + &[] as &[$i_type], + &[1 as $i_type] as &[$i_type], + &[0 as $i_type] as &[$i_type], + &[11 as $i_type] as &[$i_type], + &[11, 1 as $i_type] as &[$i_type], + &[11, 0, 1 as $i_type] as &[$i_type], + &[12, 11, 1, 2 as $i_type] as &[$i_type], + &[12, 11, 0, 1, 2 as $i_type] as &[$i_type], + &[13, 12, 11, 1, 2, 3 as $i_type] as &[$i_type], + &[13, 12, 11, 0, 1, 2, 3 as $i_type] as &[$i_type], + &[14, 13, 12, 11, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[14, 13, 12, 11, 0, 1, 2, 3, 4 as $i_type] as &[$i_type], + &[15, 14, 13, 12, 11, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[15, 14, 13, 12, 11, 0, 1, 2, 3, 4, 5 as $i_type] as &[$i_type], + &[16, 15, 14, 13, 12, 11, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[16, 15, 14, 13, 12, 11, 0, 1, 2, 3, 4, 5, 6 as $i_type] as &[$i_type], + &[17, 16, 15, 14, 13, 12, 11, 1, 2, 3, 4, 5, 6, 7 as $i_type] as &[$i_type], + &[ + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 as $i_type, + ] as &[$i_type], + &[ + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 as $i_type, + ] as &[$i_type], + &[ + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + &[ + 19, + 18, + 17, + 16, + 15, + 14, + 13, + 12, + 11, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 as $i_type, + ] as &[$i_type], + ]; + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let end_encode_func = $end_encode_func; + let end_decode_func = $end_decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = [uninit; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice); + + let end = 1i32; + end_encode_func(&mut encoder, end); + + // decode... + let buf = ReadBuf::new(buffer.as_slice()); + let header = MessageHeaderDecoder::default().wrap(buf, 0); + + let decoder = DemoDecoder::default().header(header, 0); + let decoded = decode_func(&decoder); + for each_idx in 0..effective_len { + assert_eq!( + each_slice[each_idx], + decoded[each_idx], + "Item mismatched at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], + 0, + "Item should be padded ZERO at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + let decoded_end = end_decode_func(&decoder); + assert_eq!(decoded_end, end, "End Item should equal",); + } + }; + } + + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u16_zero_padded, + DemoDecoder::fixed_16_u16, + DemoEncoder::fixed_16_u16_end, + DemoDecoder::fixed_16_u16_end, + u16 + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u32_zero_padded, + DemoDecoder::fixed_16_u32, + DemoEncoder::fixed_16_u32_end, + DemoDecoder::fixed_16_u32_end, + u32 + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u64_zero_padded, + DemoDecoder::fixed_16_u64, + DemoEncoder::fixed_16_u64_end, + DemoDecoder::fixed_16_u64_end, + u64 + ); +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/LibRsDef.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/LibRsDef.java index ae50c9bef7..39a5ed633c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/LibRsDef.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/LibRsDef.java @@ -276,7 +276,14 @@ static void generateWriteBuf(final Writer writer, final ByteOrder byteOrder) thr indent(writer, 2, "dest.clone_from_slice(src);\n"); indent(writer, 2, "len\n"); indent(writer, 1, "}\n"); + indent(writer, 0, "}\n"); + // impl From for &[u8] + indent(writer, 0, "impl<%s> From<&%1$s mut %2$s<%1$s>> for &%1$s mut [u8] {\n", BUF_LIFETIME, WRITE_BUF_TYPE); + indent(writer, 1, "#[inline]\n"); + indent(writer, 1, "fn from(buf: &%s mut %2$s<%1$s>) -> &%1$s mut [u8] {\n", BUF_LIFETIME, WRITE_BUF_TYPE); + indent(writer, 2, "buf.data\n"); + indent(writer, 1, "}\n"); indent(writer, 0, "}\n\n"); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java index f78899cb4b..d56c680d28 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java @@ -361,7 +361,62 @@ static void generateEncoderVarData( } } - private static void generatePrimitiveEncoder( + private static String rustNullLiteral(final Encoding encoding) + { + return generateRustLiteral(encoding.primitiveType(), encoding.applicableNullValue()); + } + + + private static void generateRustDoc( + final StringBuilder sb, + final int level, + final Token typeToken, + final Encoding encoding) throws IOException + { + indent(sb, level, "/// - min value: %s\n", encoding.applicableMinValue()); + indent(sb, level, "/// - max value: %s\n", encoding.applicableMaxValue()); + indent(sb, level, "/// - null value: %s\n", rustNullLiteral(encoding)); + indent(sb, level, "/// - characterEncoding: %s\n", encoding.characterEncoding()); + indent(sb, level, "/// - semanticType: %s\n", encoding.semanticType()); + indent(sb, level, "/// - encodedOffset: %d\n", typeToken.offset()); + indent(sb, level, "/// - encodedLength: %d\n", typeToken.encodedLength()); + indent(sb, level, "/// - version: %d\n", typeToken.version()); + } + + private static void generatePrimitiveArrayFromIterEncoder( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name) throws IOException + { + final Encoding encoding = typeToken.encoding(); + final PrimitiveType primitiveType = encoding.primitiveType(); + final String rustPrimitiveType = rustTypeName(primitiveType); + + indent(sb, level, "/// primitive array field '%s' from an Iterator\n", name); + generateRustDoc(sb, level, typeToken, encoding); + indent(sb, level, "#[inline]\n"); + indent(sb, level, "pub fn %s_from_iter(&mut self, iter: impl Iterator) {\n", + formatFunctionName(name), rustPrimitiveType); + + indent(sb, level + 1, "let offset = self.%s;\n", getBufOffset(typeToken)); + indent(sb, level + 1, "let buf = self.get_buf_mut();\n"); + indent(sb, level + 1, "for (i, v) in iter.enumerate() {\n"); + + if (primitiveType.size() == 1) + { + indent(sb, level + 2, "buf.put_%s_at(offset + i, v);\n", rustPrimitiveType); + } + else + { + indent(sb, level + 2, "buf.put_%s_at(offset + i * %d, v);\n", + rustPrimitiveType, primitiveType.size()); + } + indent(sb, level + 1, "}\n"); + indent(sb, level, "}\n\n"); + } + + private static void generatePrimitiveArrayZeroPaddedEncoder( final StringBuilder sb, final int level, final Token typeToken, @@ -371,34 +426,66 @@ private static void generatePrimitiveEncoder( final PrimitiveType primitiveType = encoding.primitiveType(); final String rustPrimitiveType = rustTypeName(primitiveType); final int arrayLength = typeToken.arrayLength(); - if (arrayLength > 1) + + indent(sb, level, "/// primitive array field '%s' with zero padding\n", name); + generateRustDoc(sb, level, typeToken, encoding); + indent(sb, level, "#[inline]\n"); + indent(sb, level, "pub fn %s_zero_padded(&mut self, value: &[%s]) {\n", + formatFunctionName(name), rustPrimitiveType); + + indent(sb, level + 1, "let iter = value.iter().copied().chain(std::iter::repeat(0_%s)).take(%d);\n", + rustPrimitiveType, arrayLength); + + indent(sb, level + 1, "self.%s_from_iter(iter);\n", formatFunctionName(name)); + indent(sb, level, "}\n\n"); + } + + private static void generatePrimitiveArrayEncoder( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name) throws IOException + { + final Encoding encoding = typeToken.encoding(); + final PrimitiveType primitiveType = encoding.primitiveType(); + final String rustPrimitiveType = rustTypeName(primitiveType); + final int arrayLength = typeToken.arrayLength(); + + indent(sb, level, "#[inline]\n"); + indent(sb, level, "pub fn %s_at(&mut self, index: usize, value: %s) {\n", + formatFunctionName(name), rustPrimitiveType); + indent(sb, level + 1, "let offset = self.%s;\n", getBufOffset(typeToken)); + indent(sb, level + 1, "let buf = self.get_buf_mut();\n"); + + if (primitiveType.size() == 1) { - indent(sb, level, "/// primitive array field '%s'\n", name); - indent(sb, level, "/// - min value: %s\n", encoding.applicableMinValue()); - indent(sb, level, "/// - max value: %s\n", encoding.applicableMaxValue()); - indent(sb, level, "/// - null value: %s\n", encoding.applicableNullValue()); - indent(sb, level, "/// - characterEncoding: %s\n", encoding.characterEncoding()); - indent(sb, level, "/// - semanticType: %s\n", encoding.semanticType()); - indent(sb, level, "/// - encodedOffset: %d\n", typeToken.offset()); - indent(sb, level, "/// - encodedLength: %d\n", typeToken.encodedLength()); - indent(sb, level, "/// - version: %d\n", typeToken.version()); - indent(sb, level, "#[inline]\n"); - indent(sb, level, "pub fn %s(&mut self, value: &[%s; %d]) {\n", - formatFunctionName(name), - rustPrimitiveType, - arrayLength); + indent(sb, level + 1, "buf.put_%s_at(offset + index, value);\n", rustPrimitiveType); + } + else + { + indent(sb, level + 1, "buf.put_%s_at(offset + index * %d, value);\n", + rustPrimitiveType, primitiveType.size()); + } + indent(sb, level, "}\n\n"); - // NB: must create variable 'offset' before calling mutable self.get_buf_mut() - indent(sb, level + 1, "let offset = self.%s;\n", getBufOffset(typeToken)); - indent(sb, level + 1, "let buf = self.get_buf_mut();\n"); + indent(sb, level, "/// primitive array field '%s'\n", name); + generateRustDoc(sb, level, typeToken, encoding); + indent(sb, level, "#[inline]\n"); + indent(sb, level, "pub fn %s(&mut self, value: &[%s]) {\n", + formatFunctionName(name), rustPrimitiveType); - if (rustPrimitiveType.equals("u8")) - { - indent(sb, level + 1, "buf.put_bytes_at(offset, value);\n"); - indent(sb, level, "}\n\n"); - return; - } + // NB: must create variable 'offset' before calling mutable self.get_buf_mut() + indent(sb, level + 1, "debug_assert_eq!(%d, value.len());\n", arrayLength); + indent(sb, level + 1, "let offset = self.%s;\n", getBufOffset(typeToken)); + indent(sb, level + 1, "let buf = self.get_buf_mut();\n"); + if (rustPrimitiveType.equals("u8")) + { + indent(sb, level + 1, "buf.put_slice_at(offset, value);\n"); + indent(sb, level, "}\n\n"); + } + else + { for (int i = 0; i < arrayLength; i++) { if (i == 0) @@ -416,34 +503,44 @@ private static void generatePrimitiveEncoder( indent(sb, level, "}\n\n"); } - else + + generatePrimitiveArrayFromIterEncoder(sb, level, typeToken, name); + generatePrimitiveArrayZeroPaddedEncoder(sb, level, typeToken, name); + } + + private static void generatePrimitiveEncoder( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name) throws IOException + { + final int arrayLength = typeToken.arrayLength(); + if (arrayLength > 1) { - if (encoding.presence() == Encoding.Presence.CONSTANT) - { - indent(sb, level, "// skipping CONSTANT %s\n\n", name); - } - else - { - indent(sb, level, "/// primitive field '%s'\n", name); - indent(sb, level, "/// - min value: %s\n", encoding.applicableMinValue()); - indent(sb, level, "/// - max value: %s\n", encoding.applicableMaxValue()); - indent(sb, level, "/// - null value: %s\n", encoding.applicableNullValue()); - indent(sb, level, "/// - characterEncoding: %s\n", encoding.characterEncoding()); - indent(sb, level, "/// - semanticType: %s\n", encoding.semanticType()); - indent(sb, level, "/// - encodedOffset: %d\n", typeToken.offset()); - indent(sb, level, "/// - encodedLength: %d\n", typeToken.encodedLength()); - indent(sb, level, "/// - version: %d\n", typeToken.version()); - indent(sb, level, "#[inline]\n"); - indent(sb, level, "pub fn %s(&mut self, value: %s) {\n", - formatFunctionName(name), - rustPrimitiveType); - - // NB: must create variable 'offset' before calling mutable self.get_buf_mut() - indent(sb, level + 1, "let offset = self.%s;\n", getBufOffset(typeToken)); - indent(sb, level + 1, "self.get_buf_mut().put_%s_at(offset, value);\n", rustPrimitiveType); - indent(sb, level, "}\n\n"); - } + generatePrimitiveArrayEncoder(sb, level, typeToken, name); + return; } + + final Encoding encoding = typeToken.encoding(); + if (encoding.presence() == Encoding.Presence.CONSTANT) + { + indent(sb, level, "// skipping CONSTANT %s\n\n", name); + return; + } + + final PrimitiveType primitiveType = encoding.primitiveType(); + final String rustPrimitiveType = rustTypeName(primitiveType); + + indent(sb, level, "/// primitive field '%s'\n", name); + generateRustDoc(sb, level, typeToken, encoding); + indent(sb, level, "#[inline]\n"); + indent(sb, level, "pub fn %s(&mut self, value: %s) {\n", + formatFunctionName(name), rustPrimitiveType); + + // NB: must create variable 'offset' before calling mutable self.get_buf_mut() + indent(sb, level + 1, "let offset = self.%s;\n", getBufOffset(typeToken)); + indent(sb, level + 1, "self.get_buf_mut().put_%s_at(offset, value);\n", rustPrimitiveType); + indent(sb, level, "}\n\n"); } private static void generateEnumEncoder( @@ -679,7 +776,7 @@ private static void generatePrimitiveArrayDecoder( if (fieldToken.version() > 0) { indent(sb, level + 1, "if self.acting_version() < %d {\n", fieldToken.version()); - indent(sb, level + 2, "return [%s; %d];\n", encoding.applicableNullValue(), arrayLength); + indent(sb, level + 2, "return [%s; %d];\n", rustNullLiteral(encoding), arrayLength); indent(sb, level + 1, "}\n\n"); } @@ -772,7 +869,7 @@ private static void generatePrimitiveOptionalDecoder( final String rustPrimitiveType = rustTypeName(primitiveType); final String characterEncoding = encoding.characterEncoding(); indent(sb, level, "/// primitive field - '%s' { null_value: '%s' }\n", - encoding.presence(), encoding.applicableNullValue()); + encoding.presence(), rustNullLiteral(encoding)); if (characterEncoding != null) { @@ -789,14 +886,14 @@ private static void generatePrimitiveOptionalDecoder( rustPrimitiveType, getBufOffset(fieldToken)); - final String literal = generateRustLiteral(primitiveType, encoding.applicableNullValue().toString()); - if (literal.endsWith("::NAN")) + final String nullLiteral = rustNullLiteral(encoding); + if (nullLiteral.endsWith("::NAN")) { indent(sb, level + 1, "if value.is_nan() {\n"); } else { - indent(sb, level + 1, "if value == %s {\n", literal); + indent(sb, level + 1, "if value == %s {\n", nullLiteral); } indent(sb, level + 2, "None\n"); @@ -833,8 +930,7 @@ private static void generatePrimitiveRequiredDecoder( if (fieldToken.version() > 0) { indent(sb, level + 1, "if self.acting_version() < %d {\n", fieldToken.version()); - indent(sb, level + 2, "return %s;\n", - generateRustLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString())); + indent(sb, level + 2, "return %s;\n", rustNullLiteral(encoding)); indent(sb, level + 1, "}\n\n"); } @@ -1257,8 +1353,7 @@ private static void generateEnum( // null value { final Encoding encoding = messageBody.get(0).encoding(); - final CharSequence nullVal = generateRustLiteral(encoding.primitiveType(), - encoding.applicableNullValue().toString()); + final CharSequence nullVal = rustNullLiteral(encoding); indent(writer, 1, "#[default]\n"); indent(writer, 1, "NullVal = %s, \n", nullVal); } @@ -1322,8 +1417,7 @@ private static void generateFromEnumForPrimitive( { final Encoding encoding = messageBody.get(0).encoding(); - final CharSequence nullVal = generateRustLiteral(encoding.primitiveType(), - encoding.applicableNullValue().toString()); + final CharSequence nullVal = rustNullLiteral(encoding); indent(writer, 3, "%s::NullVal => %s,\n", enumRustName, nullVal); } indent(writer, 2, "}\n"); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustUtil.java index 43b9c8ba8a..ae55de1e9e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustUtil.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustUtil.java @@ -17,6 +17,7 @@ import org.agrona.Verify; import uk.co.real_logic.sbe.PrimitiveType; +import uk.co.real_logic.sbe.PrimitiveValue; import uk.co.real_logic.sbe.generation.Generators; import uk.co.real_logic.sbe.generation.rust.RustGenerator.CodecType; import uk.co.real_logic.sbe.ir.Encoding; @@ -65,6 +66,11 @@ static String rustTypeName(final PrimitiveType primitiveType) return TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.get(primitiveType); } + static String generateRustLiteral(final PrimitiveType type, final PrimitiveValue value) + { + return generateRustLiteral(type, value.toString()); + } + static String generateRustLiteral(final PrimitiveType type, final String value) { Verify.notNull(type, "type"); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustUtilTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustUtilTest.java index d6d7652b83..6092fb5d48 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustUtilTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustUtilTest.java @@ -88,6 +88,7 @@ void generateRustLiteralsForUnsignedPrimitiveNulls() assertEquals("0xff_u8", generateRustLiteral(UINT8, UINT8.nullValue().toString())); assertEquals("0xffff_u16", generateRustLiteral(UINT16, UINT16.nullValue().toString())); assertEquals("0xffffffff_u32", generateRustLiteral(UINT32, UINT32.nullValue().toString())); + assertEquals("0xffffffffffffffff_u64", generateRustLiteral(UINT64, UINT64.nullValue())); assertEquals("0xffffffffffffffff_u64", generateRustLiteral(UINT64, UINT64.nullValue().toString())); } @@ -100,7 +101,7 @@ void generateRustLiteralNullPrimitiveTypeParam() @Test void generateRustLiteralNullValueParam() { - assertThrows(NullPointerException.class, () -> generateRustLiteral(INT8, null)); + assertThrows(NullPointerException.class, () -> generateRustLiteral(INT8, (String)null)); } @Test diff --git a/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml b/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml new file mode 100644 index 0000000000..62f3b9a5b0 --- /dev/null +++ b/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +