Skip to content

Commit

Permalink
refactor words64 type
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagofneto committed Aug 26, 2023
1 parent 9682dee commit 9cd6759
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 176 deletions.
32 changes: 16 additions & 16 deletions src/encoding/rlp_word64.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -43,15 +43,15 @@ impl RLPTypeImpl of RLPTypeTrait {
// @notice Represent a RLP item
#[derive(Drop)]
enum RLPItemWord64 {
Bytes: Span<u64>,
Bytes: Words64,
// Should be Span<RLPItem> to allow for any depth/recursion, not yet supported by the compiler
List: Span<Span<u64>>
List: Span<Words64>
}

// @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<u64>) -> 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
Expand All @@ -63,45 +63,45 @@ fn rlp_decode_word64(input: Span<u64>) -> 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))
}
}
}

fn rlp_decode_list_word64(ref input: Span<u64>, len: usize) -> Span<Span<u64>> {
fn rlp_decode_list_word64(ref input: Words64, len: usize) -> Span<Words64> {
let mut i = 0;
let mut output = ArrayTrait::new();
let mut total_len = len;
Expand All @@ -119,7 +119,7 @@ fn rlp_decode_list_word64(ref input: Span<u64>, len: usize) -> Span<Span<u64>> {
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;
},
Expand All @@ -132,8 +132,8 @@ fn rlp_decode_list_word64(ref input: Span<u64>, len: usize) -> Span<Span<u64>> {
output.span()
}

impl SpanU64PartialEq of PartialEq<Span<u64>> {
fn eq(lhs: @Span<u64>, rhs: @Span<u64>) -> bool {
impl SpanU64PartialEq of PartialEq<Words64> {
fn eq(lhs: @Words64, rhs: @Words64) -> bool {
let len_lhs = (*lhs).len();
if len_lhs != (*rhs).len() {
return false;
Expand All @@ -153,7 +153,7 @@ impl SpanU64PartialEq of PartialEq<Span<u64>> {
}
}

fn ne(lhs: @Span<u64>, rhs: @Span<u64>) -> bool {
fn ne(lhs: @Words64, rhs: @Words64) -> bool {
!(lhs == rhs)
}
}
Expand Down
97 changes: 0 additions & 97 deletions src/utils/bitwise.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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>) -> u64 {
let sb = match significant_bytes {
Option::Some(x) => x,
Expand All @@ -164,77 +141,3 @@ fn reverse_endianness(input: u64, significant_bytes: Option<u64>) -> u64 {
}
}

// len in bytes, words in le
fn slice_words64_le(input: Span<u64>, start: usize, len: usize) -> Span<u64> {
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()
}
64 changes: 1 addition & 63 deletions src/utils/tests/test_bitwise.cairo
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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');
}
1 change: 1 addition & 0 deletions src/utils/types.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod bytes;
mod byte;
mod words64;

#[cfg(test)]
mod tests;
1 change: 1 addition & 0 deletions src/utils/types/tests.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod test_bytes;
mod test_byte;
mod test_words64;
65 changes: 65 additions & 0 deletions src/utils/types/tests/test_words64.cairo
Original file line number Diff line number Diff line change
@@ -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');
}
Loading

0 comments on commit 9cd6759

Please sign in to comment.