From 9cd6759d6b2d8d64167107d13d53c11d0e3ee1ee Mon Sep 17 00:00:00 2001 From: tiagofneto Date: Sat, 26 Aug 2023 15:28:26 +0200 Subject: [PATCH] refactor words64 type --- src/encoding/rlp_word64.cairo | 32 ++++---- src/utils/bitwise.cairo | 97 ------------------------ src/utils/tests/test_bitwise.cairo | 64 +--------------- src/utils/types.cairo | 1 + src/utils/types/tests.cairo | 1 + src/utils/types/tests/test_words64.cairo | 65 ++++++++++++++++ src/utils/types/words64.cairo | 83 ++++++++++++++++++++ 7 files changed, 167 insertions(+), 176 deletions(-) create mode 100644 src/utils/types/tests/test_words64.cairo create mode 100644 src/utils/types/words64.cairo diff --git a/src/encoding/rlp_word64.cairo b/src/encoding/rlp_word64.cairo index ae5477b..e61c7b9 100644 --- a/src/encoding/rlp_word64.cairo +++ b/src/encoding/rlp_word64.cairo @@ -3,9 +3,9 @@ use option::OptionTrait; use array::{Array, ArrayTrait, Span, SpanTrait}; use clone::Clone; use traits::{Into, TryInto}; -use cairo_lib::utils::types::bytes::{Bytes, BytesPartialEq, BytesTryIntoU256}; +use cairo_lib::utils::types::words64::{Words64, Words64Trait}; use cairo_lib::utils::types::byte::Byte; -use cairo_lib::utils::bitwise::{right_shift, bytes_used, slice_words64_le, reverse_endianness}; +use cairo_lib::utils::bitwise::{right_shift, bytes_used, reverse_endianness}; use debug::PrintTrait; // @notice Enum with all possible RLP types @@ -43,15 +43,15 @@ impl RLPTypeImpl of RLPTypeTrait { // @notice Represent a RLP item #[derive(Drop)] enum RLPItemWord64 { - Bytes: Span, + Bytes: Words64, // Should be Span to allow for any depth/recursion, not yet supported by the compiler - List: Span> + List: Span } // @notice RLP decodes a rlp encoded byte array // @param input RLP encoded bytes // @return Result with RLPItem and size of the decoded item -fn rlp_decode_word64(input: Span) -> Result<(RLPItemWord64, usize), felt252> { +fn rlp_decode_word64(input: Words64) -> Result<(RLPItemWord64, usize), felt252> { let prefix: u8 = (*input.at(0) & 0xff).try_into().unwrap(); // Unwrap is impossible to panic here @@ -63,37 +63,37 @@ fn rlp_decode_word64(input: Span) -> Result<(RLPItemWord64, usize), felt252 }, RLPType::StringShort(()) => { let len = prefix.into() - 0x80; - let res = slice_words64_le(input, 6, len); + let res = input.slice_le(6, len); Result::Ok((RLPItemWord64::Bytes(res), 1 + len)) }, RLPType::StringLong(()) => { let len_len = prefix.into() - 0xb7; - let len_span = slice_words64_le(input, 6, len_len); + let len_span = input.slice_le(6, len_len); // Enough to store 4.29 GB (fits in u32) assert(len_span.len() == 1 && *len_span.at(0) <= 0xffffffff, 'Len of len too big'); // len fits in 32 bits, confirmed by previous assertion let len: u32 = reverse_endianness(*len_span.at(0), Option::Some(len_len.into())).try_into().unwrap(); - let res = slice_words64_le(input, 6 - len_len, len); + let res = input.slice_le(6 - len_len, len); Result::Ok((RLPItemWord64::Bytes(res), 1 + len_len + len)) }, RLPType::ListShort(()) => { let mut len = prefix.into() - 0xc0; - let mut in = slice_words64_le(input, 6, len); + let mut in = input.slice_le(6, len); let res = rlp_decode_list_word64(ref in, len); Result::Ok((RLPItemWord64::List(res), 1 + len)) }, RLPType::ListLong(()) => { let len_len = prefix.into() - 0xf7; - let len_span = slice_words64_le(input, 6, len_len); + let len_span = input.slice_le(6, len_len); // Enough to store 4.29 GB (fits in u32) assert(len_span.len() == 1 && *len_span.at(0) <= 0xffffffff, 'Len of len too big'); // len fits in 32 bits, confirmed by previous assertion let len: u32 = reverse_endianness(*len_span.at(0), Option::Some(len_len.into())).try_into().unwrap(); - let mut in = slice_words64_le(input, 6 - len_len, len); + let mut in = input.slice_le(6 - len_len, len); let res = rlp_decode_list_word64(ref in, len); Result::Ok((RLPItemWord64::List(res), 1 + len_len + len)) @@ -101,7 +101,7 @@ fn rlp_decode_word64(input: Span) -> Result<(RLPItemWord64, usize), felt252 } } -fn rlp_decode_list_word64(ref input: Span, len: usize) -> Span> { +fn rlp_decode_list_word64(ref input: Words64, len: usize) -> Span { let mut i = 0; let mut output = ArrayTrait::new(); let mut total_len = len; @@ -119,7 +119,7 @@ fn rlp_decode_list_word64(ref input: Span, len: usize) -> Span> { let reversed = 7 - (decoded_len % 8); let next_start = word * 8 + reversed; if (total_len - decoded_len != 0) { - input = slice_words64_le(input, next_start, total_len - decoded_len); + input = input.slice_le(next_start, total_len - decoded_len); } total_len -= decoded_len; }, @@ -132,8 +132,8 @@ fn rlp_decode_list_word64(ref input: Span, len: usize) -> Span> { output.span() } -impl SpanU64PartialEq of PartialEq> { - fn eq(lhs: @Span, rhs: @Span) -> bool { +impl SpanU64PartialEq of PartialEq { + fn eq(lhs: @Words64, rhs: @Words64) -> bool { let len_lhs = (*lhs).len(); if len_lhs != (*rhs).len() { return false; @@ -153,7 +153,7 @@ impl SpanU64PartialEq of PartialEq> { } } - fn ne(lhs: @Span, rhs: @Span) -> bool { + fn ne(lhs: @Words64, rhs: @Words64) -> bool { !(lhs == rhs) } } diff --git a/src/utils/bitwise.cairo b/src/utils/bitwise.cairo index e46c925..cb1fc72 100644 --- a/src/utils/bitwise.cairo +++ b/src/utils/bitwise.cairo @@ -121,29 +121,6 @@ fn bytes_used(val: u64) -> usize { } } -//fn extract_byte_at(input: u64, index: u64) -> Byte { - //let shift = right_shift(input, (56 - (index * 8))); - //(shift & 0xff).try_into().unwrap() -//} - -//fn remove_bytes_from_start(input: u64, num_bytes: u64) -> u64 { - //left_shift(input, (num_bytes * 8)) -//} - -//fn add_bytes_to_start(input: u64, bytes: u64, num_bytes: u64) -> u64 { - //let shift = left_shift(input, (num_bytes * 8)); - //let mask = left_shift(1, (num_bytes * 8)) - 1; - - //shift | (bytes & mask) -//} - -//fn add_bytes_to_end(input: u64, bytes: u64, num_bytes: u64) -> u64 { - //let shift = left_shift(input, (num_bytes * 8)); - //let mask = left_shift(1, (num_bytes * 8)) - 1; - - //shift | (bytes & mask) -//} - fn reverse_endianness(input: u64, significant_bytes: Option) -> u64 { let sb = match significant_bytes { Option::Some(x) => x, @@ -164,77 +141,3 @@ fn reverse_endianness(input: u64, significant_bytes: Option) -> u64 { } } -// len in bytes, words in le -fn slice_words64_le(input: Span, start: usize, len: usize) -> Span { - if len == 0 { - return ArrayTrait::new().span(); - } - - let first_word_index = start / 8; - // number of right bytes to remove - let mut word_offset = 8 - ((start+1) % 8); - if word_offset == 8 { - word_offset = 0; - } - - let mut output_words = len / 8; - if len % 8 != 0 { - output_words += 1; - } - - let mut output = ArrayTrait::new(); - let mut i = first_word_index; - loop { - if i - first_word_index == output_words - 1 { - break (); - } - let word = *input.at(i); - let next = *input.at(i+1); - - // remove lsb bytes from the first word - let shifted = right_shift_u64(word, word_offset.into() * 8); - - // get lsb bytes from the second word - let mask_second_word = left_shift_u64(1, word_offset * 8) - 1; - let bytes_to_append = next & mask_second_word.into(); - - // apend bytes to msb first word - let mask_first_word = left_shift_u64(bytes_to_append, (8 - word_offset.into()) * 8); - let new_word = shifted | mask_first_word; - - output.append(new_word); - i += 1; - }; - - - let last_word = *input.at(i); - let shifted = right_shift_u64(last_word, word_offset.into() * 8); - - let mut len_last_word = len % 8; - if len_last_word == 0 { - len_last_word = 8; - } - - if len_last_word <= 8 - word_offset { - // using u128 because if len_last_word == 8 left_shift might overflow by 1 - // after subtracting 1 it's safe to unwrap - let mask: u128 = left_shift(1_u128, len_last_word.into() * 8) - 1; - let last_word_masked = shifted & mask.try_into().unwrap(); - output.append(last_word_masked); - } else { - let missing_bytes = len_last_word - (8 - word_offset); - let next = *input.at(i+1); - - // get lsb bytes from the second word - let mask_second_word = left_shift_u64(1, missing_bytes * 8) - 1; - let bytes_to_append = next & mask_second_word.into(); - - // apend bytes to msb first word - let mask_first_word = left_shift_u64(bytes_to_append, (8 - word_offset.into()) * 8); - let new_word = shifted | mask_first_word; - - output.append(new_word); - } - - output.span() -} diff --git a/src/utils/tests/test_bitwise.cairo b/src/utils/tests/test_bitwise.cairo index 29d20fc..8394fd3 100644 --- a/src/utils/tests/test_bitwise.cairo +++ b/src/utils/tests/test_bitwise.cairo @@ -1,4 +1,4 @@ -use cairo_lib::utils::bitwise::{left_shift, right_shift, bit_length, slice_words64_le}; +use cairo_lib::utils::bitwise::{left_shift, right_shift, bit_length}; use array::{ArrayTrait, SpanTrait}; use debug::PrintTrait; use option::OptionTrait; @@ -36,65 +36,3 @@ fn test_bit_length() { assert(bit_length(8_u32) == 4, 'bit length of 8 is 4'); } -#[test] -#[available_gas(99999999)] -fn test_slice_words64_le_multiple_words_not_full() { - let val = array![ - 0xabcdef1234567890, - 0x7584934785943295, - 0x48542576 - ].span(); - - let res = slice_words64_le(val, 5, 17); - assert(res.len() == 3, 'Wrong len'); - assert(*res.at(0) == 0x3295abcdef123456, 'Wrong value at 0'); - assert(*res.at(1) == 0x2576758493478594, 'Wrong value at 1'); - assert(*res.at(2) == 0x54, 'Wrong value at 2'); -} - -#[test] -#[available_gas(99999999)] -fn test_slice_words64_le_multiple_words_full() { - let val = array![ - 0xabcdef1234567890, - 0x7584934785943295, - 0x48542576 - ].span(); - - -let gas = testing::get_available_gas(); - let res = slice_words64_le(val, 4, 16); - assert(res.len() == 2, 'Wrong len'); - assert(*res.at(0) == 0x943295abcdef1234, 'Wrong value at 0'); - assert(*res.at(1) == 0x5425767584934785, 'Wrong value at 1'); -} - -#[test] -#[available_gas(99999999)] -fn test_slice_words64_le_single_word_not_full() { - let val = array![ - 0xabcdef1234567890, - 0x7584934785943295, - 0x48542576 - ].span(); - - - let res = slice_words64_le(val, 1, 5); - assert(res.len() == 1, 'Wrong len'); - assert(*res.at(0) == 0x943295abcd, 'Wrong value at 0'); -} - -#[test] -#[available_gas(99999999)] -fn test_slice_words64_le_single_word_full() { - let val = array![ - 0xabcdef1234567890, - 0x7584934785943295, - 0x48542576 - ].span(); - - - let res = slice_words64_le(val, 15, 8); - assert(res.len() == 1, 'Wrong len'); - assert(*res.at(0) == 0x7584934785943295, 'Wrong value at 0'); -} diff --git a/src/utils/types.cairo b/src/utils/types.cairo index ea4cdf4..c46a3bc 100644 --- a/src/utils/types.cairo +++ b/src/utils/types.cairo @@ -1,5 +1,6 @@ mod bytes; mod byte; +mod words64; #[cfg(test)] mod tests; diff --git a/src/utils/types/tests.cairo b/src/utils/types/tests.cairo index 399643b..1476dc8 100644 --- a/src/utils/types/tests.cairo +++ b/src/utils/types/tests.cairo @@ -1,2 +1,3 @@ mod test_bytes; mod test_byte; +mod test_words64; diff --git a/src/utils/types/tests/test_words64.cairo b/src/utils/types/tests/test_words64.cairo new file mode 100644 index 0000000..00093a0 --- /dev/null +++ b/src/utils/types/tests/test_words64.cairo @@ -0,0 +1,65 @@ +use cairo_lib::utils::types::words64::{Words64, Words64Trait}; +use array::{ArrayTrait, SpanTrait}; + +#[test] +#[available_gas(99999999)] +fn test_slice_words64_le_multiple_words_not_full() { + let val: Words64 = array![ + 0xabcdef1234567890, + 0x7584934785943295, + 0x48542576 + ].span(); + + let res = val.slice_le(5, 17); + assert(res.len() == 3, 'Wrong len'); + assert(*res.at(0) == 0x3295abcdef123456, 'Wrong value at 0'); + assert(*res.at(1) == 0x2576758493478594, 'Wrong value at 1'); + assert(*res.at(2) == 0x54, 'Wrong value at 2'); +} + +#[test] +#[available_gas(99999999)] +fn test_slice_words64_le_multiple_words_full() { + let val: Words64 = array![ + 0xabcdef1234567890, + 0x7584934785943295, + 0x48542576 + ].span(); + + +let gas = testing::get_available_gas(); + let res = val.slice_le(4, 16); + assert(res.len() == 2, 'Wrong len'); + assert(*res.at(0) == 0x943295abcdef1234, 'Wrong value at 0'); + assert(*res.at(1) == 0x5425767584934785, 'Wrong value at 1'); +} + +#[test] +#[available_gas(99999999)] +fn test_slice_words64_le_single_word_not_full() { + let val: Words64 = array![ + 0xabcdef1234567890, + 0x7584934785943295, + 0x48542576 + ].span(); + + + let res = val.slice_le(1, 5); + assert(res.len() == 1, 'Wrong len'); + assert(*res.at(0) == 0x943295abcd, 'Wrong value at 0'); +} + +#[test] +#[available_gas(99999999)] +fn test_slice_words64_le_single_word_full() { + let val: Words64 = array![ + 0xabcdef1234567890, + 0x7584934785943295, + 0x48542576 + ].span(); + + + let res = val.slice_le(15, 8); + assert(res.len() == 1, 'Wrong len'); + assert(*res.at(0) == 0x7584934785943295, 'Wrong value at 0'); +} diff --git a/src/utils/types/words64.cairo b/src/utils/types/words64.cairo new file mode 100644 index 0000000..b51b572 --- /dev/null +++ b/src/utils/types/words64.cairo @@ -0,0 +1,83 @@ +use array::{ArrayTrait, SpanTrait}; +use cairo_lib::utils::bitwise::{right_shift_u64, left_shift_u64, left_shift}; +use traits::{Into, TryInto}; +use option::OptionTrait; + +type Words64 = Span; + +#[generate_trait] +impl Words64Impl of Words64Trait { + fn slice_le(self: Words64, start: usize, len: usize) -> Span { + if len == 0 { + return ArrayTrait::new().span(); + } + + let first_word_index = start / 8; + // number of right bytes to remove + let mut word_offset = 8 - ((start+1) % 8); + if word_offset == 8 { + word_offset = 0; + } + + let mut output_words = len / 8; + if len % 8 != 0 { + output_words += 1; + } + + let mut output = ArrayTrait::new(); + let mut i = first_word_index; + loop { + if i - first_word_index == output_words - 1 { + break (); + } + let word = *self.at(i); + let next = *self.at(i+1); + + // remove lsb bytes from the first word + let shifted = right_shift_u64(word, word_offset.into() * 8); + + // get lsb bytes from the second word + let mask_second_word = left_shift_u64(1, word_offset * 8) - 1; + let bytes_to_append = next & mask_second_word.into(); + + // apend bytes to msb first word + let mask_first_word = left_shift_u64(bytes_to_append, (8 - word_offset.into()) * 8); + let new_word = shifted | mask_first_word; + + output.append(new_word); + i += 1; + }; + + + let last_word = *self.at(i); + let shifted = right_shift_u64(last_word, word_offset.into() * 8); + + let mut len_last_word = len % 8; + if len_last_word == 0 { + len_last_word = 8; + } + + if len_last_word <= 8 - word_offset { + // using u128 because if len_last_word == 8 left_shift might overflow by 1 + // after subtracting 1 it's safe to unwrap + let mask: u128 = left_shift(1_u128, len_last_word.into() * 8) - 1; + let last_word_masked = shifted & mask.try_into().unwrap(); + output.append(last_word_masked); + } else { + let missing_bytes = len_last_word - (8 - word_offset); + let next = *self.at(i+1); + + // get lsb bytes from the second word + let mask_second_word = left_shift_u64(1, missing_bytes * 8) - 1; + let bytes_to_append = next & mask_second_word.into(); + + // apend bytes to msb first word + let mask_first_word = left_shift_u64(bytes_to_append, (8 - word_offset.into()) * 8); + let new_word = shifted | mask_first_word; + + output.append(new_word); + } + + output.span() + } +}