From 7d6dfc4adacd8700ad57cf629d73d3f189352ea9 Mon Sep 17 00:00:00 2001 From: Elvis Wang Date: Fri, 15 Nov 2024 16:37:18 +0800 Subject: [PATCH 1/3] [Rust] generate at_most_*_items_from_slice setter for fixed sized primitive array in message --- build.gradle | 1 + rust/Cargo.toml | 1 + rust/tests/fixed_sized_primitive_array.rs | 511 ++++++++++++++++++ .../sbe/generation/rust/RustGenerator.java | 200 +++++-- .../fixed-sized-primitive-array-types.xml | 58 ++ 5 files changed, 729 insertions(+), 42 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 6a20ee7201..59a1f41042 100644 --- a/build.gradle +++ b/build.gradle @@ -654,6 +654,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/fixed_sized_primitive_array.rs b/rust/tests/fixed_sized_primitive_array.rs new file mode 100644 index 0000000000..dd5380e641 --- /dev/null +++ b/rust/tests/fixed_sized_primitive_array.rs @@ -0,0 +1,511 @@ +use fixed_sized_primitive_array::{ + demo_codec::{DemoDecoder, DemoEncoder}, + message_header_codec::{MessageHeaderDecoder, ENCODED_LENGTH}, + ReadBuf, WriteBuf, +}; + +fn create_encoder(buffer: &mut Vec) -> DemoEncoder { + let encoder = DemoEncoder::default().wrap(WriteBuf::new(buffer.as_mut_slice()), ENCODED_LENGTH); + let mut header = encoder.header(0); + header.parent().unwrap() +} + +#[test] +fn test_encode_then_decode_u8_slice() { + 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, $i_null:expr) => { + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = vec![1u8; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice); + + // 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 + ); + } + let null_value = $i_null; + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], null_value, + "Item should be NULL at {}/{}", + each_idx, cur_len + ); + } + } + }; + } + + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_char_at_most_16_items_from_slice, + DemoDecoder::fixed_16_char, + 0 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_char_at_most_16_items_from_slice, + DemoDecoder::fixed_16_ascii_char, + 0 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_char_at_most_16_items_from_slice, + DemoDecoder::fixed_16_gb_18030_char, + 0 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_char_at_most_16_items_from_slice, + DemoDecoder::fixed_16_utf_8_char, + 0 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_u8_at_most_16_items_from_slice, + DemoDecoder::fixed_16_u8, + u8::MAX + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_u8_at_most_16_items_from_slice, + DemoDecoder::fixed_16_ascii_u8, + u8::MAX + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_u8_at_most_16_items_from_slice, + DemoDecoder::fixed_16_gb_18030_u8, + u8::MAX + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_u8_at_most_16_items_from_slice, + DemoDecoder::fixed_16_utf_8_u8, + u8::MAX + ); +} + +#[test] +fn test_encode_then_decode_non_u8_signed_primitive_slice() { + // + // + // + // + macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { + ($encode_func:expr, $decode_func:expr, $i_type:ty, $i_null: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], + ]; + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = vec![1u8; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, &each_slice); + + // 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) + ); + } + let null_value = $i_null; + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], + null_value, + "Item should be null at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + } + }; + } + + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_i8_at_most_16_items_from_slice, + DemoDecoder::fixed_16_i8, + i8, + i8::MIN + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_i16_at_most_16_items_from_slice, + DemoDecoder::fixed_16_i16, + i16, + i16::MIN + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_i32_at_most_16_items_from_slice, + DemoDecoder::fixed_16_i32, + i32, + i32::MIN + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_i64_at_most_16_items_from_slice, + DemoDecoder::fixed_16_i64, + i64, + i64::MIN + ); +} + +#[test] +fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { + // + // + macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { + ($encode_func:expr, $decode_func:expr, $i_type:ty, $i_null: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], + ]; + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = vec![1u8; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, &each_slice); + + // 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) + ); + } + let null_value = $i_null; + for each_idx in effective_len..16 { + assert_eq!( + decoded[each_idx], + null_value, + "Item should be null at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + } + }; + } + + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_u16_at_most_16_items_from_slice, + DemoDecoder::fixed_16_u16, + u16, + u16::MAX + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_u32_at_most_16_items_from_slice, + DemoDecoder::fixed_16_u32, + u32, + u32::MAX + ); + // run_encode_then_decode_for_array_of_u8_len_16!(DemoEncoder::fixed_16_u64_at_most_16_items_from_slice, DemoDecoder::fixed_16_i64, i64, u64::MAX); +} 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..6a17fd6611 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 @@ -373,48 +373,10 @@ private static void generatePrimitiveEncoder( final int arrayLength = typeToken.arrayLength(); if (arrayLength > 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); - - // 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"); - - if (rustPrimitiveType.equals("u8")) - { - indent(sb, level + 1, "buf.put_bytes_at(offset, value);\n"); - indent(sb, level, "}\n\n"); - return; - } - - for (int i = 0; i < arrayLength; i++) - { - if (i == 0) - { - indent(sb, level + 1, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); - } - else - { - indent(sb, level + 1, "buf.put_%s_at(offset + %d, value[%d]);\n", - rustPrimitiveType, - i * primitiveType.size(), - i); - } - } - - indent(sb, level, "}\n\n"); + generatePrimitiveArrayEncoderWithRef( + sb, level, typeToken, name, encoding, primitiveType, rustPrimitiveType); + generatePrimitiveArrayEncoderWithSliceRef( + sb, level, typeToken, name, encoding, primitiveType, rustPrimitiveType); } else { @@ -446,6 +408,160 @@ private static void generatePrimitiveEncoder( } } + private static void generatePrimitiveArrayEncoderWithRef( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name, + final Encoding encoding, + final PrimitiveType primitiveType, + final String rustPrimitiveType) throws IOException + { + final int arrayLength = typeToken.arrayLength(); + 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); + + // 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"); + + if (rustPrimitiveType.equals("u8")) + { + indent(sb, level + 1, "buf.put_bytes_at(offset, value);\n"); + indent(sb, level, "}\n\n"); + return; + } + + for (int i = 0; i < arrayLength; i++) + { + if (i == 0) + { + indent(sb, level + 1, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); + } + else + { + indent(sb, level + 1, "buf.put_%s_at(offset + %d, value[%d]);\n", + rustPrimitiveType, i * primitiveType.size(), i); + } + } + + indent(sb, level, "}\n\n"); + } + + private static void generatePrimitiveArrayEncoderWithSliceRef( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name, + final Encoding encoding, + final PrimitiveType primitiveType, + final String rustPrimitiveType) throws IOException + { + final int arrayLength = typeToken.arrayLength(); + indent(sb, level, "/// primitive array field '%s': copy at most %d items from slice\n", name, arrayLength); + 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_at_most_%d_items_from_slice(&mut self, value: &[%s]) {\n", + formatFunctionName(name), arrayLength, 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, "let buf = self.get_buf_mut();\n"); + + indent(sb, level + 1, "match value.len() {\n"); + for (int matchedLen = 0; matchedLen <= arrayLength; matchedLen++) + { + if (matchedLen == arrayLength) + { + indent(sb, level + 2, "_ => {\n"); + } + else + { + indent(sb, level + 2, "%d => {\n", matchedLen); + } + indent(sb, level + 3, "// Copy all %d items from 'value' into '%s' of [%s; %d] \n", + matchedLen, name, rustPrimitiveType, arrayLength); + if (matchedLen != 0) + { + if (rustPrimitiveType.equals("u8")) + { + indent(sb, level + 3, "buf.put_slice_at(offset, value);\n"); + } + else + { + for (int i = 0; i < matchedLen; i++) + { + if (i == 0) + { + indent(sb, level + 3, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); + } + else + { + indent(sb, level + 3, "buf.put_%s_at(offset + %d, value[%d]);\n", + rustPrimitiveType, i * primitiveType.size(), i); + } + } + } + } + indent(sb, level + 3, "\n"); + indent(sb, level + 3, "// Set %d left items of '%s' to '%s' (aka NULL)\n", + arrayLength - matchedLen, name, encoding.applicableNullValue()); + if (matchedLen != arrayLength) + { + if (rustPrimitiveType.equals("u8")) + { + if (matchedLen == 0) + { + indent(sb, level + 3, "buf.put_bytes_at(offset, &[%s; %d]);\n", + encoding.applicableNullValue(), arrayLength - matchedLen); + } + else + { + indent(sb, level + 3, "buf.put_bytes_at(offset + %d, &[%s; %d]);\n", + matchedLen, encoding.applicableNullValue(), arrayLength - matchedLen); + } + } + else + { + for (int i = matchedLen; i < arrayLength; i++) + { + if (i == 0) + { + indent(sb, level + 3, "buf.put_%s_at(offset, %s);\n", + rustPrimitiveType, encoding.applicableNullValue()); + } + else + { + indent(sb, level + 3, "buf.put_%s_at(offset + %d, %s);\n", + rustPrimitiveType, i * primitiveType.size(), encoding.applicableNullValue()); + } + } + } + } + indent(sb, level + 2, "}\n"); + } + indent(sb, level + 1, "}\n"); + + indent(sb, level, "}\n\n"); + } + private static void generateEnumEncoder( final StringBuilder sb, final int level, 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..719715d6b5 --- /dev/null +++ b/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3d074eb159a9727ccb2a391d4c51106fbdddad81 Mon Sep 17 00:00:00 2001 From: Elvis Wang Date: Fri, 15 Nov 2024 17:52:31 +0800 Subject: [PATCH 2/3] [Rust] add padded support for slice setter for primitive array field --- rust/tests/fixed_sized_primitive_array.rs | 597 ++++++++++++++++-- .../sbe/generation/rust/RustGenerator.java | 196 ++++-- .../fixed-sized-primitive-array-types.xml | 4 +- 3 files changed, 686 insertions(+), 111 deletions(-) diff --git a/rust/tests/fixed_sized_primitive_array.rs b/rust/tests/fixed_sized_primitive_array.rs index dd5380e641..440afd7b89 100644 --- a/rust/tests/fixed_sized_primitive_array.rs +++ b/rust/tests/fixed_sized_primitive_array.rs @@ -58,7 +58,7 @@ fn test_encode_then_decode_u8_slice() { // // macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { - ($encode_func:expr, $decode_func:expr, $i_null:expr) => { + ($encode_func:expr, $decode_func:expr) => { for each_slice in test_data { let encode_func = $encode_func; let decode_func = $decode_func; @@ -67,7 +67,7 @@ fn test_encode_then_decode_u8_slice() { let effective_len = cur_len.min(16); // encode... - let mut buffer = vec![1u8; 1024]; + let mut buffer = vec![0u8; 1024]; let mut encoder = create_encoder(&mut buffer); encode_func(&mut encoder, each_slice); @@ -85,11 +85,10 @@ fn test_encode_then_decode_u8_slice() { each_idx, cur_len ); } - let null_value = $i_null; for each_idx in effective_len..16 { assert_eq!( - decoded[each_idx], null_value, - "Item should be NULL at {}/{}", + decoded[each_idx], 0, + "Item should be ZERO at {}/{}", each_idx, cur_len ); } @@ -99,43 +98,35 @@ fn test_encode_then_decode_u8_slice() { run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_char_at_most_16_items_from_slice, - DemoDecoder::fixed_16_char, - 0 + DemoDecoder::fixed_16_char ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_ascii_char_at_most_16_items_from_slice, - DemoDecoder::fixed_16_ascii_char, - 0 + DemoDecoder::fixed_16_ascii_char ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_gb_18030_char_at_most_16_items_from_slice, - DemoDecoder::fixed_16_gb_18030_char, - 0 + DemoDecoder::fixed_16_gb_18030_char ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_utf_8_char_at_most_16_items_from_slice, - DemoDecoder::fixed_16_utf_8_char, - 0 + DemoDecoder::fixed_16_utf_8_char ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_u8_at_most_16_items_from_slice, - DemoDecoder::fixed_16_u8, - u8::MAX + DemoDecoder::fixed_16_u8 ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_ascii_u8_at_most_16_items_from_slice, - DemoDecoder::fixed_16_ascii_u8, - u8::MAX + DemoDecoder::fixed_16_ascii_u8 ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_gb_18030_u8_at_most_16_items_from_slice, - DemoDecoder::fixed_16_gb_18030_u8, - u8::MAX + DemoDecoder::fixed_16_gb_18030_u8 ); run_encode_then_decode_for_array_of_u8_len_16!( DemoEncoder::fixed_16_utf_8_u8_at_most_16_items_from_slice, - DemoDecoder::fixed_16_utf_8_u8, - u8::MAX + DemoDecoder::fixed_16_utf_8_u8 ); } @@ -145,8 +136,8 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { // // // - macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { - ($encode_func:expr, $decode_func:expr, $i_type:ty, $i_null:expr) => { + macro_rules! run_encode_then_decode_for_array_of_signed_len_16 { + ($encode_func:expr, $decode_func:expr, $i_type:ty) => { let test_data = [ &[] as &[$i_type], &[1 as $i_type] as &[$i_type], @@ -269,7 +260,7 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { let effective_len = cur_len.min(16); // encode... - let mut buffer = vec![1u8; 1024]; + let mut buffer = vec![0u8; 1024]; let mut encoder = create_encoder(&mut buffer); encode_func(&mut encoder, &each_slice); @@ -290,12 +281,11 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { stringify!($i_type) ); } - let null_value = $i_null; for each_idx in effective_len..16 { assert_eq!( decoded[each_idx], - null_value, - "Item should be null at {}/{} for {}", + 0, + "Item should be ZERO at {}/{} for {}", each_idx, cur_len, stringify!($i_type) @@ -305,29 +295,25 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { }; } - run_encode_then_decode_for_array_of_u8_len_16!( + run_encode_then_decode_for_array_of_signed_len_16!( DemoEncoder::fixed_16_i8_at_most_16_items_from_slice, DemoDecoder::fixed_16_i8, - i8, - i8::MIN + i8 ); - run_encode_then_decode_for_array_of_u8_len_16!( + run_encode_then_decode_for_array_of_signed_len_16!( DemoEncoder::fixed_16_i16_at_most_16_items_from_slice, DemoDecoder::fixed_16_i16, - i16, - i16::MIN + i16 ); - run_encode_then_decode_for_array_of_u8_len_16!( + run_encode_then_decode_for_array_of_signed_len_16!( DemoEncoder::fixed_16_i32_at_most_16_items_from_slice, DemoDecoder::fixed_16_i32, - i32, - i32::MIN + i32 ); - run_encode_then_decode_for_array_of_u8_len_16!( + run_encode_then_decode_for_array_of_signed_len_16!( DemoEncoder::fixed_16_i64_at_most_16_items_from_slice, DemoDecoder::fixed_16_i64, - i64, - i64::MIN + i64 ); } @@ -335,8 +321,8 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { // // - macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { - ($encode_func:expr, $decode_func:expr, $i_type:ty, $i_null:expr) => { + macro_rules! run_encode_then_decode_for_array_of_unsigned_len_16 { + ($encode_func:expr, $decode_func:expr, $i_type:ty) => { let test_data = [ &[] as &[$i_type], &[1 as $i_type] as &[$i_type], @@ -459,10 +445,10 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { let effective_len = cur_len.min(16); // encode... - let mut buffer = vec![1u8; 1024]; + let mut buffer = vec![0u8; 1024]; let mut encoder = create_encoder(&mut buffer); - encode_func(&mut encoder, &each_slice); + encode_func(&mut encoder, each_slice); // decode... let buf = ReadBuf::new(buffer.as_slice()); @@ -480,12 +466,11 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { stringify!($i_type) ); } - let null_value = $i_null; for each_idx in effective_len..16 { assert_eq!( decoded[each_idx], - null_value, - "Item should be null at {}/{} for {}", + 0, + "Item should be ZERO at {}/{} for {}", each_idx, cur_len, stringify!($i_type) @@ -495,17 +480,525 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { }; } - run_encode_then_decode_for_array_of_u8_len_16!( + run_encode_then_decode_for_array_of_unsigned_len_16!( DemoEncoder::fixed_16_u16_at_most_16_items_from_slice, DemoDecoder::fixed_16_u16, - u16, - u16::MAX + u16 ); - run_encode_then_decode_for_array_of_u8_len_16!( + run_encode_then_decode_for_array_of_unsigned_len_16!( DemoEncoder::fixed_16_u32_at_most_16_items_from_slice, DemoDecoder::fixed_16_u32, + u32 + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u64_at_most_16_items_from_slice, + DemoDecoder::fixed_16_u64, + u64 + ); +} + +#[test] +fn test_encode_then_decode_u8_slice_padded() { + 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, $i_padded:expr) => { + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let padded_value = $i_padded; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = vec![0u8; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice, padded_value); + + // 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], padded_value, + "Item should be padded at {}/{}", + each_idx, cur_len + ); + } + } + }; + } + + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_char_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_char, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_char_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_ascii_char, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_char_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_gb_18030_char, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_char_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_utf_8_char, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_u8_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_u8, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_ascii_u8_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_ascii_u8, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_gb_18030_u8_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_gb_18030_u8, + 30 + ); + run_encode_then_decode_for_array_of_u8_len_16!( + DemoEncoder::fixed_16_utf_8_u8_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_utf_8_u8, + 30 + ); +} + +#[test] +fn test_encode_then_decode_non_u8_signed_primitive_slice_padded() { + // + // + // + // + macro_rules! run_encode_then_decode_for_array_of_signed_len_16 { + ($encode_func:expr, $decode_func:expr, $i_type:ty, $i_padded: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], + ]; + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let padded_value = $i_padded; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = vec![0u8; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice, padded_value); + + // 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], + padded_value, + "Item should be padded at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + } + }; + } + + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i8_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_i8, + i8, + -31 + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i16_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_i16, + i16, + -31 + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i32_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_i32, + i32, + -31 + ); + run_encode_then_decode_for_array_of_signed_len_16!( + DemoEncoder::fixed_16_i64_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_i64, + i64, + -31 + ); +} + +#[test] +fn test_encode_then_decode_non_u8_unsigned_primitive_slice_padded() { + // + // + // + macro_rules! run_encode_then_decode_for_array_of_unsigned_len_16 { + ($encode_func:expr, $decode_func:expr, $i_type:ty, $i_padded: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], + ]; + for each_slice in test_data { + let encode_func = $encode_func; + let decode_func = $decode_func; + let padded_value = $i_padded; + + let cur_len = each_slice.len(); + let effective_len = cur_len.min(16); + + // encode... + let mut buffer = vec![0u8; 1024]; + let mut encoder = create_encoder(&mut buffer); + + encode_func(&mut encoder, each_slice, padded_value); + + // 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], + padded_value, + "Item should be padded at {}/{} for {}", + each_idx, + cur_len, + stringify!($i_type) + ); + } + } + }; + } + + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u16_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_u16, + u16, + 1024u16 + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u32_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_u32, u32, - u32::MAX + 10000000u32 + ); + run_encode_then_decode_for_array_of_unsigned_len_16!( + DemoEncoder::fixed_16_u64_at_most_16_items_from_slice_padded, + DemoDecoder::fixed_16_u64, + u64, + 20241115173301u64 ); - // run_encode_then_decode_for_array_of_u8_len_16!(DemoEncoder::fixed_16_u64_at_most_16_items_from_slice, DemoDecoder::fixed_16_i64, i64, u64::MAX); } 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 6a17fd6611..f4c8a41ac0 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 @@ -376,7 +376,9 @@ private static void generatePrimitiveEncoder( generatePrimitiveArrayEncoderWithRef( sb, level, typeToken, name, encoding, primitiveType, rustPrimitiveType); generatePrimitiveArrayEncoderWithSliceRef( - sb, level, typeToken, name, encoding, primitiveType, rustPrimitiveType); + sb, level, typeToken, name, encoding, primitiveType, rustPrimitiveType, false); + generatePrimitiveArrayEncoderWithSliceRef( + sb, level, typeToken, name, encoding, primitiveType, rustPrimitiveType, true); } else { @@ -458,32 +460,15 @@ private static void generatePrimitiveArrayEncoderWithRef( indent(sb, level, "}\n\n"); } - private static void generatePrimitiveArrayEncoderWithSliceRef( + private static void generatePrimitiveArrayEncoderWithSliceRefMethodBodyForU8( final StringBuilder sb, final int level, final Token typeToken, final String name, - final Encoding encoding, - final PrimitiveType primitiveType, - final String rustPrimitiveType) throws IOException + final String rustPrimitiveType, + final String paddedName) throws IOException { final int arrayLength = typeToken.arrayLength(); - indent(sb, level, "/// primitive array field '%s': copy at most %d items from slice\n", name, arrayLength); - 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_at_most_%d_items_from_slice(&mut self, value: &[%s]) {\n", - formatFunctionName(name), arrayLength, 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, "let buf = self.get_buf_mut();\n"); indent(sb, level + 1, "match value.len() {\n"); for (int matchedLen = 0; matchedLen <= arrayLength; matchedLen++) @@ -500,64 +485,161 @@ private static void generatePrimitiveArrayEncoderWithSliceRef( matchedLen, name, rustPrimitiveType, arrayLength); if (matchedLen != 0) { - if (rustPrimitiveType.equals("u8")) + indent(sb, level + 3, "buf.put_slice_at(offset, value);\n"); + } + + indent(sb, level + 3, "\n"); + + if (paddedName.isEmpty()) + { + indent(sb, level + 3, "// Leave %d left items unchanged\n", arrayLength - matchedLen); + indent(sb, level + 2, "}\n"); + continue; + } + + indent(sb, level + 3, "// Pad %d left items of '%s' with parameter \"%s\"\n", + arrayLength - matchedLen, name, paddedName); + if (matchedLen != arrayLength) + { + if (matchedLen == 0) { - indent(sb, level + 3, "buf.put_slice_at(offset, value);\n"); + indent(sb, level + 3, "buf.put_bytes_at(offset, &[%s; %d]);\n", + paddedName, arrayLength - matchedLen); } else { - for (int i = 0; i < matchedLen; i++) - { - if (i == 0) - { - indent(sb, level + 3, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); - } - else - { - indent(sb, level + 3, "buf.put_%s_at(offset + %d, value[%d]);\n", - rustPrimitiveType, i * primitiveType.size(), i); - } - } + indent(sb, level + 3, "buf.put_bytes_at(offset + %d, &[%s; %d]);\n", + matchedLen, paddedName, arrayLength - matchedLen); } } - indent(sb, level + 3, "\n"); - indent(sb, level + 3, "// Set %d left items of '%s' to '%s' (aka NULL)\n", - arrayLength - matchedLen, name, encoding.applicableNullValue()); - if (matchedLen != arrayLength) + indent(sb, level + 2, "}\n"); + } + indent(sb, level + 1, "}\n"); + } + + private static void generatePrimitiveArrayEncoderWithSliceRefMethodBodyForNonU8( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name, + final PrimitiveType primitiveType, + final String rustPrimitiveType, + final String paddedName) throws IOException + { + final int arrayLength = typeToken.arrayLength(); + + indent(sb, level + 1, "match value.len() {\n"); + for (int matchedLen = 0; matchedLen <= arrayLength; matchedLen++) + { + if (matchedLen == arrayLength) { - if (rustPrimitiveType.equals("u8")) + indent(sb, level + 2, "_ => {\n"); + } + else + { + indent(sb, level + 2, "%d => {\n", matchedLen); + } + indent(sb, level + 3, "// Copy all %d items from 'value' into '%s' of [%s; %d] \n", + matchedLen, name, rustPrimitiveType, arrayLength); + if (matchedLen != 0) + { + for (int i = 0; i < matchedLen; i++) { - if (matchedLen == 0) + if (i == 0) { - indent(sb, level + 3, "buf.put_bytes_at(offset, &[%s; %d]);\n", - encoding.applicableNullValue(), arrayLength - matchedLen); + indent(sb, level + 3, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); } else { - indent(sb, level + 3, "buf.put_bytes_at(offset + %d, &[%s; %d]);\n", - matchedLen, encoding.applicableNullValue(), arrayLength - matchedLen); + indent(sb, level + 3, "buf.put_%s_at(offset + %d, value[%d]);\n", + rustPrimitiveType, i * primitiveType.size(), i); } } - else + } + + indent(sb, level + 3, "\n"); + + if (paddedName.isEmpty()) + { + indent(sb, level + 3, "// Leave %d left items unchanged\n", arrayLength - matchedLen); + indent(sb, level + 2, "}\n"); + continue; + } + + indent(sb, level + 3, "// Pad %d left items of '%s' with parameter \"%s\"\n", + arrayLength - matchedLen, name, paddedName); + if (matchedLen != arrayLength) + { + for (int i = matchedLen; i < arrayLength; i++) { - for (int i = matchedLen; i < arrayLength; i++) + if (i == 0) + { + indent(sb, level + 3, "buf.put_%s_at(offset, %s);\n", + rustPrimitiveType, paddedName); + } + else { - if (i == 0) - { - indent(sb, level + 3, "buf.put_%s_at(offset, %s);\n", - rustPrimitiveType, encoding.applicableNullValue()); - } - else - { - indent(sb, level + 3, "buf.put_%s_at(offset + %d, %s);\n", - rustPrimitiveType, i * primitiveType.size(), encoding.applicableNullValue()); - } + indent(sb, level + 3, "buf.put_%s_at(offset + %d, %s);\n", + rustPrimitiveType, i * primitiveType.size(), paddedName); } } } indent(sb, level + 2, "}\n"); } indent(sb, level + 1, "}\n"); + } + + private static void generatePrimitiveArrayEncoderWithSliceRef( + final StringBuilder sb, + final int level, + final Token typeToken, + final String name, + final Encoding encoding, + final PrimitiveType primitiveType, + final String rustPrimitiveType, + final boolean withPadding) throws IOException + { + final int arrayLength = typeToken.arrayLength(); + indent(sb, level, "/// primitive array field '%s': copy at most %d items from slice %s padding\n", + name, arrayLength, withPadding ? "with" : "without"); + 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"); + + final String paddedName; + if (withPadding) + { + paddedName = "padded"; + indent(sb, level, "pub fn %s_at_most_%d_items_from_slice_padded(&mut self, value: &[%s], %4$s: %3$s) {\n", + formatFunctionName(name), arrayLength, rustPrimitiveType, paddedName); + } + else + { + paddedName = ""; + indent(sb, level, "pub fn %s_at_most_%d_items_from_slice(&mut self, value: &[%s]) {\n", + formatFunctionName(name), arrayLength, 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, "let buf = self.get_buf_mut();\n"); + + if (rustPrimitiveType.equals("u8")) + { + generatePrimitiveArrayEncoderWithSliceRefMethodBodyForU8( + sb, level, typeToken, name, rustPrimitiveType, paddedName); + } + else + { + generatePrimitiveArrayEncoderWithSliceRefMethodBodyForNonU8( + sb, level, typeToken, name, primitiveType, rustPrimitiveType, paddedName); + } indent(sb, level, "}\n\n"); } 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 index 719715d6b5..359f1becd3 100644 --- a/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml +++ b/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml @@ -32,7 +32,7 @@ - + @@ -53,6 +53,6 @@ - + From 1a41d6de28d3deda3bce3f18e76012bafae4e10a Mon Sep 17 00:00:00 2001 From: Elvis Wang Date: Mon, 16 Dec 2024 15:44:08 +0800 Subject: [PATCH 3/3] [Rust] simplify impl of generated slice aware methods --- rust/tests/fixed_sized_primitive_array.rs | 266 ++++++++++++------ .../sbe/generation/rust/RustGenerator.java | 223 ++++++++------- .../fixed-sized-primitive-array-types.xml | 39 ++- 3 files changed, 342 insertions(+), 186 deletions(-) diff --git a/rust/tests/fixed_sized_primitive_array.rs b/rust/tests/fixed_sized_primitive_array.rs index 440afd7b89..43e82f347d 100644 --- a/rust/tests/fixed_sized_primitive_array.rs +++ b/rust/tests/fixed_sized_primitive_array.rs @@ -4,14 +4,16 @@ use fixed_sized_primitive_array::{ ReadBuf, WriteBuf, }; -fn create_encoder(buffer: &mut Vec) -> DemoEncoder { - let encoder = DemoEncoder::default().wrap(WriteBuf::new(buffer.as_mut_slice()), ENCODED_LENGTH); +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], @@ -58,20 +60,25 @@ fn test_encode_then_decode_u8_slice() { // // macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { - ($encode_func:expr, $decode_func:expr) => { + ($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 = vec![0u8; 1024]; + 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); @@ -87,57 +94,77 @@ fn test_encode_then_decode_u8_slice() { } for each_idx in effective_len..16 { assert_eq!( - decoded[each_idx], 0, - "Item should be ZERO at {}/{}", + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_char + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_ascii_char + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_gb_18030_char + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_utf_8_char + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_u8 + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_ascii_u8 + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_gb_18030_u8 + 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_at_most_16_items_from_slice, - DemoDecoder::fixed_16_utf_8_u8 + 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, $i_type:ty) => { + ($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], @@ -252,19 +279,27 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { 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 = vec![0u8; 1024]; + 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); @@ -284,13 +319,15 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { for each_idx in effective_len..16 { assert_eq!( decoded[each_idx], - 0, - "Item should be ZERO at {}/{} for {}", + 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",); } }; } @@ -298,31 +335,41 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice() { run_encode_then_decode_for_array_of_signed_len_16!( DemoEncoder::fixed_16_i8_at_most_16_items_from_slice, DemoDecoder::fixed_16_i8, - 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_at_most_16_items_from_slice, DemoDecoder::fixed_16_i16, - 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_at_most_16_items_from_slice, DemoDecoder::fixed_16_i32, - 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_at_most_16_items_from_slice, DemoDecoder::fixed_16_i64, - 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, $i_type:ty) => { + ($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], @@ -437,19 +484,27 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { 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 = vec![0u8; 1024]; + 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); @@ -466,16 +521,19 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { stringify!($i_type) ); } + for each_idx in effective_len..16 { assert_eq!( decoded[each_idx], - 0, - "Item should be ZERO at {}/{} for {}", + 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",); } }; } @@ -483,22 +541,33 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice() { run_encode_then_decode_for_array_of_unsigned_len_16!( DemoEncoder::fixed_16_u16_at_most_16_items_from_slice, DemoDecoder::fixed_16_u16, - 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_at_most_16_items_from_slice, DemoDecoder::fixed_16_u32, - 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_at_most_16_items_from_slice, DemoDecoder::fixed_16_u64, - 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], @@ -545,20 +614,24 @@ fn test_encode_then_decode_u8_slice_padded() { // // macro_rules! run_encode_then_decode_for_array_of_u8_len_16 { - ($encode_func:expr, $decode_func:expr, $i_padded:expr) => { + ($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 padded_value = $i_padded; + 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 = vec![0u8; 1024]; + let mut buffer = [uninit; 1024]; let mut encoder = create_encoder(&mut buffer); - encode_func(&mut encoder, each_slice, padded_value); + encode_func(&mut encoder, each_slice); + + let end = 1i32; + end_encode_func(&mut encoder, end); // decode... let buf = ReadBuf::new(buffer.as_slice()); @@ -575,65 +648,77 @@ fn test_encode_then_decode_u8_slice_padded() { } for each_idx in effective_len..16 { assert_eq!( - decoded[each_idx], padded_value, - "Item should be padded at {}/{}", + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_char_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_char, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_ascii_char_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_ascii_char, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_gb_18030_char_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_gb_18030_char, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_utf_8_char_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_utf_8_char, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_u8_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_u8, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_ascii_u8_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_ascii_u8, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_gb_18030_u8_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_gb_18030_u8, - 30 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_utf_8_u8_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_utf_8_u8, - 30 + 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, $i_type:ty, $i_padded:expr) => { + ($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], @@ -751,16 +836,20 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice_padded() { for each_slice in test_data { let encode_func = $encode_func; let decode_func = $decode_func; - let padded_value = $i_padded; + 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 = vec![0u8; 1024]; + let mut buffer = [uninit; 1024]; let mut encoder = create_encoder(&mut buffer); - encode_func(&mut encoder, each_slice, padded_value); + encode_func(&mut encoder, each_slice); + + let end = 1i32; + end_encode_func(&mut encoder, end); // decode... let buf = ReadBuf::new(buffer.as_slice()); @@ -781,50 +870,58 @@ fn test_encode_then_decode_non_u8_signed_primitive_slice_padded() { for each_idx in effective_len..16 { assert_eq!( decoded[each_idx], - padded_value, - "Item should be padded at {}/{} for {}", + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_i8_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_i8, + DemoEncoder::fixed_16_i8_end, + DemoDecoder::fixed_16_i8_end, i8, - -31 ); run_encode_then_decode_for_array_of_signed_len_16!( - DemoEncoder::fixed_16_i16_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_i16_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_i16, + DemoEncoder::fixed_16_i16_end, + DemoDecoder::fixed_16_i16_end, i16, - -31 ); run_encode_then_decode_for_array_of_signed_len_16!( - DemoEncoder::fixed_16_i32_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_i32_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_i32, + DemoEncoder::fixed_16_i32_end, + DemoDecoder::fixed_16_i32_end, i32, - -31 ); run_encode_then_decode_for_array_of_signed_len_16!( - DemoEncoder::fixed_16_i64_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_i64_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_i64, + DemoEncoder::fixed_16_i64_end, + DemoDecoder::fixed_16_i64_end, i64, - -31 ); } #[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, $i_type:ty, $i_padded:expr) => { + ($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], @@ -942,16 +1039,20 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice_padded() { for each_slice in test_data { let encode_func = $encode_func; let decode_func = $decode_func; - let padded_value = $i_padded; + 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 = vec![0u8; 1024]; + let mut buffer = [uninit; 1024]; let mut encoder = create_encoder(&mut buffer); - encode_func(&mut encoder, each_slice, padded_value); + encode_func(&mut encoder, each_slice); + + let end = 1i32; + end_encode_func(&mut encoder, end); // decode... let buf = ReadBuf::new(buffer.as_slice()); @@ -972,33 +1073,38 @@ fn test_encode_then_decode_non_u8_unsigned_primitive_slice_padded() { for each_idx in effective_len..16 { assert_eq!( decoded[each_idx], - padded_value, - "Item should be padded at {}/{} for {}", + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_u16_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_u16, - u16, - 1024u16 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_u32_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_u32, - u32, - 10000000u32 + 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_at_most_16_items_from_slice_padded, + DemoEncoder::fixed_16_u64_at_most_16_items_from_slice_padded_with_zero, DemoDecoder::fixed_16_u64, - u64, - 20241115173301u64 + 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/RustGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java index f4c8a41ac0..2e4a4d5499 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 @@ -466,54 +466,54 @@ private static void generatePrimitiveArrayEncoderWithSliceRefMethodBodyForU8( final Token typeToken, final String name, final String rustPrimitiveType, - final String paddedName) throws IOException + final boolean withPadding) throws IOException { final int arrayLength = typeToken.arrayLength(); - indent(sb, level + 1, "match value.len() {\n"); - for (int matchedLen = 0; matchedLen <= arrayLength; matchedLen++) + indent(sb, level + 1, "match len {\n"); + + // Handle case when slice len is greater than 0 and less than {@code arrayLength} + indent(sb, level + 2, "(1..%d) => {\n", arrayLength); + indent(sb, level + 3, "// Copy all items from 'value' into '%s' of [%s; %d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "buf.put_slice_at(offset, value);\n"); + if (!withPadding) { - if (matchedLen == arrayLength) - { - indent(sb, level + 2, "_ => {\n"); - } - else - { - indent(sb, level + 2, "%d => {\n", matchedLen); - } - indent(sb, level + 3, "// Copy all %d items from 'value' into '%s' of [%s; %d] \n", - matchedLen, name, rustPrimitiveType, arrayLength); - if (matchedLen != 0) - { - indent(sb, level + 3, "buf.put_slice_at(offset, value);\n"); - } + indent(sb, level + 3, "// Leave all left items as is (no padding) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + } + else + { + indent(sb, level + 3, "// Pad all left items with ZERO(0%2$s) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "const ZEROS: [%1$s; %2$d] = [0%1$s; %2$d];\n", rustPrimitiveType, arrayLength); + indent(sb, level + 3, "buf.put_slice_at(offset + len, &ZEROS[len..]);\n"); + } + indent(sb, level + 2, "}\n"); - indent(sb, level + 3, "\n"); + // Handle case when slice is empty + indent(sb, level + 2, "0 => {\n"); + if (!withPadding) + { + indent(sb, level + 3, "// Leave all %3$d items as is (no padding) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + } + else + { + indent(sb, level + 3, "// Pad all %3$d items with ZERO(0%2$s) in '%s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "const ZEROS: [%1$s; %2$d] = [0%1$s; %2$d];\n", rustPrimitiveType, arrayLength); + indent(sb, level + 3, "buf.put_bytes_at(offset, &ZEROS);\n"); + } + indent(sb, level + 2, "}\n"); - if (paddedName.isEmpty()) - { - indent(sb, level + 3, "// Leave %d left items unchanged\n", arrayLength - matchedLen); - indent(sb, level + 2, "}\n"); - continue; - } + // Handle case when slice len is equal to or greater than {@code arrayLength} + indent(sb, level + 2, "_ /* >= %d */ => {\n", arrayLength); + indent(sb, level + 3, "// Copy at most %3$d items from 'value' into '%s' of [%s; %d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "buf.put_slice_at(offset, &value[..%d]);\n", arrayLength); + indent(sb, level + 2, "}\n"); - indent(sb, level + 3, "// Pad %d left items of '%s' with parameter \"%s\"\n", - arrayLength - matchedLen, name, paddedName); - if (matchedLen != arrayLength) - { - if (matchedLen == 0) - { - indent(sb, level + 3, "buf.put_bytes_at(offset, &[%s; %d]);\n", - paddedName, arrayLength - matchedLen); - } - else - { - indent(sb, level + 3, "buf.put_bytes_at(offset + %d, &[%s; %d]);\n", - matchedLen, paddedName, arrayLength - matchedLen); - } - } - indent(sb, level + 2, "}\n"); - } indent(sb, level + 1, "}\n"); } @@ -524,68 +524,105 @@ private static void generatePrimitiveArrayEncoderWithSliceRefMethodBodyForNonU8( final String name, final PrimitiveType primitiveType, final String rustPrimitiveType, - final String paddedName) throws IOException + final boolean withPadding) throws IOException { final int arrayLength = typeToken.arrayLength(); - indent(sb, level + 1, "match value.len() {\n"); - for (int matchedLen = 0; matchedLen <= arrayLength; matchedLen++) + indent(sb, level + 1, "match len {\n"); + + // Handle case when slice len is greater than 1 and less than {@code arrayLength} + if (arrayLength > 2) { - if (matchedLen == arrayLength) + indent(sb, level + 2, "(2..%d) => {\n", arrayLength); + indent(sb, level + 3, "// Copy all items from 'value' into '%s' of [%s; %d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "for each_i in 0..len {\n"); + indent(sb, level + 4, "buf.put_%s_at(offset + %d * each_i, value[each_i]);\n", + rustPrimitiveType, primitiveType.size()); + indent(sb, level + 3, "}\n"); + if (!withPadding) { - indent(sb, level + 2, "_ => {\n"); + indent(sb, level + 3, "// Leave all left items as is (no padding) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); } else { - indent(sb, level + 2, "%d => {\n", matchedLen); + indent(sb, level + 3, "// Pad all left items with ZERO(0%2$s) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "for each_i in len..%d {\n", arrayLength); + indent(sb, level + 4, "buf.put_%s_at(offset + %d * each_i, 0%1$s);\n", + rustPrimitiveType, primitiveType.size()); + indent(sb, level + 3, "}\n"); + } + indent(sb, level + 2, "}\n"); + } + + // Handle case when slice len is 1 (eliminate the need of loop) + indent(sb, level + 2, "1 => {\n"); + indent(sb, level + 3, "// Copy first 1 items from 'value' into '%s' of [%s; %d]\n", + name, rustPrimitiveType, arrayLength); + indent(sb, level + 3, "buf.put_%s_at(offset, value[0]);\n", rustPrimitiveType); + if (!withPadding) + { + indent(sb, level + 3, "// Leave all left %4$d items as is (no padding) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength, arrayLength - 1); + } + else + { + indent(sb, level + 3, "// Pad all left %4$d items with ZERO(0%2$s) in '%s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength, arrayLength - 1); + for (int i = 1; i < arrayLength; i++) + { + indent(sb, level + 3, "buf.put_%s_at(offset + %d, 0%1$s);\n", + rustPrimitiveType, i * primitiveType.size()); } - indent(sb, level + 3, "// Copy all %d items from 'value' into '%s' of [%s; %d] \n", - matchedLen, name, rustPrimitiveType, arrayLength); - if (matchedLen != 0) + } + indent(sb, level + 2, "}\n"); + + // Handle case when slice is empty + indent(sb, level + 2, "0 => {\n"); + if (!withPadding) + { + indent(sb, level + 3, "// Leave all %3$d items as is (no padding) in '%1$s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + } + else + { + indent(sb, level + 3, "// Pad all %3$d items with ZERO(0%2$s) in '%s' of [%2$s; %3$d]\n", + name, rustPrimitiveType, arrayLength); + for (int i = 0; i < arrayLength; i++) { - for (int i = 0; i < matchedLen; i++) + if (i == 0) { - if (i == 0) - { - indent(sb, level + 3, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); - } - else - { - indent(sb, level + 3, "buf.put_%s_at(offset + %d, value[%d]);\n", - rustPrimitiveType, i * primitiveType.size(), i); - } + indent(sb, level + 3, "buf.put_%s_at(offset, 0%1$s);\n", rustPrimitiveType); + } + else + { + indent(sb, level + 3, "buf.put_%s_at(offset + %d, 0%1$s);\n", + rustPrimitiveType, i * primitiveType.size()); } } + } + indent(sb, level + 2, "}\n"); - indent(sb, level + 3, "\n"); - - if (paddedName.isEmpty()) + // Handle case when slice len is equal to or greater than {@code arrayLength} + indent(sb, level + 2, "_ /* >= %d */ => {\n", arrayLength); + indent(sb, level + 3, "// Copy at most %3$d items from 'value' into '%s' of [%s; %d]\n", + name, rustPrimitiveType, arrayLength); + for (int i = 0; i < arrayLength; i++) + { + if (i == 0) { - indent(sb, level + 3, "// Leave %d left items unchanged\n", arrayLength - matchedLen); - indent(sb, level + 2, "}\n"); - continue; + indent(sb, level + 3, "buf.put_%s_at(offset, value[%d]);\n", rustPrimitiveType, i); } - - indent(sb, level + 3, "// Pad %d left items of '%s' with parameter \"%s\"\n", - arrayLength - matchedLen, name, paddedName); - if (matchedLen != arrayLength) + else { - for (int i = matchedLen; i < arrayLength; i++) - { - if (i == 0) - { - indent(sb, level + 3, "buf.put_%s_at(offset, %s);\n", - rustPrimitiveType, paddedName); - } - else - { - indent(sb, level + 3, "buf.put_%s_at(offset + %d, %s);\n", - rustPrimitiveType, i * primitiveType.size(), paddedName); - } - } + indent(sb, level + 3, "buf.put_%s_at(offset + %d, value[%d]);\n", + rustPrimitiveType, i * primitiveType.size(), i); } - indent(sb, level + 2, "}\n"); } + indent(sb, level + 2, "}\n"); + indent(sb, level + 1, "}\n"); } @@ -600,7 +637,7 @@ private static void generatePrimitiveArrayEncoderWithSliceRef( final boolean withPadding) throws IOException { final int arrayLength = typeToken.arrayLength(); - indent(sb, level, "/// primitive array field '%s': copy at most %d items from slice %s padding\n", + indent(sb, level, "/// primitive array field '%s': copy at most %d items from slice %s padding ZEROs\n", name, arrayLength, withPadding ? "with" : "without"); indent(sb, level, "/// - min value: %s\n", encoding.applicableMinValue()); indent(sb, level, "/// - max value: %s\n", encoding.applicableMaxValue()); @@ -612,33 +649,31 @@ private static void generatePrimitiveArrayEncoderWithSliceRef( indent(sb, level, "/// - version: %d\n", typeToken.version()); indent(sb, level, "#[inline]\n"); - final String paddedName; - if (withPadding) + if (!withPadding) { - paddedName = "padded"; - indent(sb, level, "pub fn %s_at_most_%d_items_from_slice_padded(&mut self, value: &[%s], %4$s: %3$s) {\n", - formatFunctionName(name), arrayLength, rustPrimitiveType, paddedName); + indent(sb, level, "pub fn %s_at_most_%d_items_from_slice(&mut self, value: &[%s]) {\n", + formatFunctionName(name), arrayLength, rustPrimitiveType); } else { - paddedName = ""; - indent(sb, level, "pub fn %s_at_most_%d_items_from_slice(&mut self, value: &[%s]) {\n", + indent(sb, level, "pub fn %s_at_most_%d_items_from_slice_padded_with_zero(&mut self, value: &[%s]) {\n", formatFunctionName(name), arrayLength, 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, "let buf = self.get_buf_mut();\n"); + indent(sb, level + 1, "let len = value.len();\n"); if (rustPrimitiveType.equals("u8")) { generatePrimitiveArrayEncoderWithSliceRefMethodBodyForU8( - sb, level, typeToken, name, rustPrimitiveType, paddedName); + sb, level, typeToken, name, rustPrimitiveType, withPadding); } else { generatePrimitiveArrayEncoderWithSliceRefMethodBodyForNonU8( - sb, level, typeToken, name, primitiveType, rustPrimitiveType, paddedName); + sb, level, typeToken, name, primitiveType, rustPrimitiveType, withPadding); } indent(sb, level, "}\n\n"); 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 index 359f1becd3..62f3b9a5b0 100644 --- a/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml +++ b/sbe-tool/src/test/resources/fixed-sized-primitive-array-types.xml @@ -37,22 +37,37 @@ - - - + + + + + + + - - - + + + + + + + - - - + + + + + + + - - - + + + + + +