Skip to content

Commit

Permalink
Feat: Little endian byte order support for ByteArrayReader (#196)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

## Pull Request type

<!-- Please try to limit your pull request to one type; submit multiple
pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no API changes)
- [ ] Build-related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or
link to a relevant issue. -->

Issue Number: N/A

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Adds little endian byte order support for reading bytes from a
ByteArray

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this does introduce a breaking change, please describe the
impact and migration path for existing applications below. -->

## Other information

Depends on changes made to `byte_array_ext.cairo` submitted in a
previous PR
<!-- Any other information that is important to this PR, such as
screenshots of how the component looks before and after the change. -->
  • Loading branch information
sveamarcus authored Oct 20, 2023
1 parent 0c01185 commit 6434ba2
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 11 deletions.
124 changes: 114 additions & 10 deletions src/data_structures/src/byte_array_reader.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -25,50 +25,90 @@ trait ByteArrayReaderTrait {
/// # Returns
/// `Option<u8>` - If there are enough bytes remaining an optional integer is returned
fn read_u8(ref self: ByteArrayReader) -> Option<u8>;
/// Reads a u16 unsigned integer
/// Reads a u16 unsigned integer in big endian byte order
/// # Returns
/// `Option<u16>` - If there are enough bytes remaining an optional integer is returned
fn read_u16(ref self: ByteArrayReader) -> Option<u16>;
/// Reads a u32 unsigned integer
/// Reads a u16 unsigned integer in little endian byte order
/// # Returns
/// `Option<u16>` - If there are enough bytes remaining an optional integer is returned
fn read_u16_le(ref self: ByteArrayReader) -> Option<u16>;
/// Reads a u32 unsigned integer in big endian byte order
/// # Returns
/// `Option<u32>` - If there are enough bytes remaining an optional integer is returned
fn read_u32(ref self: ByteArrayReader) -> Option<u32>;
/// Reads a u64 unsigned integer
/// Reads a u32 unsigned integer in little endian byte order
/// # Returns
/// `Option<u32>` - If there are enough bytes remaining an optional integer is returned
fn read_u32_le(ref self: ByteArrayReader) -> Option<u32>;
/// Reads a u64 unsigned integer in big endian byte order
/// # Returns
/// `Option<u64>` - If there are enough bytes remaining an optional integer is returned
fn read_u64(ref self: ByteArrayReader) -> Option<u64>;
/// Reads a u128 unsigned integer
/// Reads a u64 unsigned integer in little endian byte order
/// # Returns
/// `Option<u64>` - If there are enough bytes remaining an optional integer is returned
fn read_u64_le(ref self: ByteArrayReader) -> Option<u64>;
/// Reads a u128 unsigned integer in big endian byte order
/// # Returns
/// `Option<u218>` - If there are enough bytes remaining an optional integer is returned
fn read_u128(ref self: ByteArrayReader) -> Option<u128>;
/// Reads a u256 unsigned integer
/// Reads a u128 unsigned integer in little endian byte order
/// # Returns
/// `Option<u218>` - If there are enough bytes remaining an optional integer is returned
fn read_u128_le(ref self: ByteArrayReader) -> Option<u128>;
/// Reads a u256 unsigned integer in big endian byte order
/// # Returns
/// `Option<u256>` - If there are enough bytes remaining an optional integer is returned
fn read_u256(ref self: ByteArrayReader) -> Option<u256>;
/// Reads a u512 unsigned integer
/// Reads a u256 unsigned integer in little endian byte order
/// # Returns
/// `Option<u256>` - If there are enough bytes remaining an optional integer is returned
fn read_u256_le(ref self: ByteArrayReader) -> Option<u256>;
/// Reads a u512 unsigned integer in big endian byte order
/// # Returns
/// `Option<u512>` - If there are enough bytes remaining an optional integer is returned
fn read_u512(ref self: ByteArrayReader) -> Option<u512>;
/// Reads a u512 unsigned integer in little endian byte order
/// # Returns
/// `Option<u512>` - If there are enough bytes remaining an optional integer is returned
fn read_u512_le(ref self: ByteArrayReader) -> Option<u512>;
/// Reads an i8 signed integer in two's complement encoding from the ByteArray
/// # Returns
/// `Option<i8>` - If there are enough bytes remaining an optional integer is returned
fn read_i8(ref self: ByteArrayReader) -> Option<i8>;
/// Reads an i16 signed integer in two's complement encoding from the ByteArray
/// Reads an i16 signed integer in two's complement encoding from the ByteArray in big endian byte order
/// # Returns
/// `Option<i16>` - If there are enough bytes remaining an optional integer is returned
fn read_i16(ref self: ByteArrayReader) -> Option<i16>;
/// Reads an i32 signed integer in two's complement encoding from the ByteArray
/// Reads an i16 signed integer in two's complement encoding from the ByteArray in little endian byte order
/// # Returns
/// `Option<i16>` - If there are enough bytes remaining an optional integer is returned
fn read_i16_le(ref self: ByteArrayReader) -> Option<i16>;
/// Reads an i32 signed integer in two's complement encoding from the ByteArray in big endian byte order
/// # Returns
/// `Option<i32>` - If there are enough bytes remaining an optional integer is returned
fn read_i32(ref self: ByteArrayReader) -> Option<i32>;
/// Reads an i64 signed integer in two's complement encoding from the ByteArray
/// Reads an i32 signed integer in two's complement encoding from the ByteArray in little endian byte order
/// # Returns
/// `Option<i32>` - If there are enough bytes remaining an optional integer is returned
fn read_i32_le(ref self: ByteArrayReader) -> Option<i32>;
/// Reads an i64 signed integer in two's complement encoding from the ByteArray in big endian byte order
/// # Returns
/// `Option<i64>` - If there are enough bytes remaining an optional integer is returned
fn read_i64(ref self: ByteArrayReader) -> Option<i64>;
/// Reads an i128 signed integer in two's complement encoding from the ByteArray
/// Reads an i64 signed integer in two's complement encoding from the ByteArray in little endian byte order
/// # Returns
/// `Option<i64>` - If there are enough bytes remaining an optional integer is returned
fn read_i64_le(ref self: ByteArrayReader) -> Option<i64>;
/// Reads an i128 signed integer in two's complement encoding from the ByteArray in big endian byte order
/// # Returns
/// `Option<i128>` - If there are enough bytes remaining an optional integer is returned
fn read_i128(ref self: ByteArrayReader) -> Option<i128>;
/// Reads an i128 signed integer in two's complement encoding from the ByteArray in little endian byte order
/// # Returns
/// `Option<i128>` - If there are enough bytes remaining an optional integer is returned
fn read_i128_le(ref self: ByteArrayReader) -> Option<i128>;
/// Returns the remaining length of the ByteArrayReader
/// # Returns
/// `usize` - The number of bytes remaining, considering the number of bytes that have already been consumed
Expand All @@ -92,24 +132,48 @@ impl ByteArrayReaderImpl of ByteArrayReaderTrait {
Option::Some(result)
}

fn read_u16_le(ref self: ByteArrayReader) -> Option<u16> {
let result = self.data.word_u16_le(self.reader_index)?;
self.reader_index += 2;
Option::Some(result)
}

fn read_u32(ref self: ByteArrayReader) -> Option<u32> {
let result = self.data.word_u32(self.reader_index)?;
self.reader_index += 4;
Option::Some(result)
}

fn read_u32_le(ref self: ByteArrayReader) -> Option<u32> {
let result = self.data.word_u32_le(self.reader_index)?;
self.reader_index += 4;
Option::Some(result)
}

fn read_u64(ref self: ByteArrayReader) -> Option<u64> {
let result = self.data.word_u64(self.reader_index)?;
self.reader_index += 8;
Option::Some(result)
}

fn read_u64_le(ref self: ByteArrayReader) -> Option<u64> {
let result = self.data.word_u64_le(self.reader_index)?;
self.reader_index += 8;
Option::Some(result)
}

fn read_u128(ref self: ByteArrayReader) -> Option<u128> {
let result = self.data.word_u128(self.reader_index)?;
self.reader_index += 16;
Option::Some(result)
}

fn read_u128_le(ref self: ByteArrayReader) -> Option<u128> {
let result = self.data.word_u128_le(self.reader_index)?;
self.reader_index += 16;
Option::Some(result)
}

fn read_u256(ref self: ByteArrayReader) -> Option<u256> {
let result = u256 {
high: self.data.word_u128(self.reader_index)?,
Expand All @@ -119,6 +183,15 @@ impl ByteArrayReaderImpl of ByteArrayReaderTrait {
Option::Some(result)
}

fn read_u256_le(ref self: ByteArrayReader) -> Option<u256> {
let result = u256 {
low: self.data.word_u128_le(self.reader_index)?,
high: self.data.word_u128_le(self.reader_index + 16)?
};
self.reader_index += 32;
Option::Some(result)
}

fn read_u512(ref self: ByteArrayReader) -> Option<u512> {
let result = u512 {
limb3: self.data.word_u128(self.reader_index)?,
Expand All @@ -130,6 +203,17 @@ impl ByteArrayReaderImpl of ByteArrayReaderTrait {
Option::Some(result)
}

fn read_u512_le(ref self: ByteArrayReader) -> Option<u512> {
let result = u512 {
limb0: self.data.word_u128_le(self.reader_index)?,
limb1: self.data.word_u128_le(self.reader_index + 16)?,
limb2: self.data.word_u128_le(self.reader_index + 32)?,
limb3: self.data.word_u128_le(self.reader_index + 48)?
};
self.reader_index += 64;
Option::Some(result)
}

fn read_i8(ref self: ByteArrayReader) -> Option<i8> {
let felt: felt252 = self.read_u8()?.into();
Option::Some(parse_signed(felt, 1).unwrap())
Expand All @@ -140,21 +224,41 @@ impl ByteArrayReaderImpl of ByteArrayReaderTrait {
Option::Some(parse_signed(felt, 2).unwrap())
}

fn read_i16_le(ref self: ByteArrayReader) -> Option<i16> {
let felt: felt252 = self.read_u16_le()?.into();
Option::Some(parse_signed(felt, 2).unwrap())
}

fn read_i32(ref self: ByteArrayReader) -> Option<i32> {
let felt: felt252 = self.read_u32()?.into();
Option::Some(parse_signed(felt, 4).unwrap())
}

fn read_i32_le(ref self: ByteArrayReader) -> Option<i32> {
let felt: felt252 = self.read_u32_le()?.into();
Option::Some(parse_signed(felt, 4).unwrap())
}

fn read_i64(ref self: ByteArrayReader) -> Option<i64> {
let felt: felt252 = self.read_u64()?.into();
Option::Some(parse_signed(felt, 8).unwrap())
}

fn read_i64_le(ref self: ByteArrayReader) -> Option<i64> {
let felt: felt252 = self.read_u64_le()?.into();
Option::Some(parse_signed(felt, 8).unwrap())
}

fn read_i128(ref self: ByteArrayReader) -> Option<i128> {
let felt: felt252 = self.read_u128()?.into();
Option::Some(parse_signed(felt, 16).unwrap())
}

fn read_i128_le(ref self: ByteArrayReader) -> Option<i128> {
let felt: felt252 = self.read_u128_le()?.into();
Option::Some(parse_signed(felt, 16).unwrap())
}

fn len(self: @ByteArrayReader) -> usize {
let byte_array = *self.data;
let byte_array_len = byte_array.len();
Expand Down
45 changes: 44 additions & 1 deletion src/data_structures/src/tests/byte_array_reader_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ fn test_read() {
let ba = test_byte_array_64();
let mut rd = ba.reader();
assert(rd.read_i8() == Option::Some(1), 'expected 1');
// rd.read_i128().unwrap().print();
assert(
rd.read_i128() == Option::Some(0x02030405060708090a0b0c0d0e0f1011), 'not 0x0203040506...'
);
Expand All @@ -60,6 +59,28 @@ fn test_read() {
assert(rd.read_u8().is_none(), 'expected none');
}

#[test]
#[available_gas(20000000)]
fn test_read_le() {
let ba = test_byte_array_64();
let mut rd = ba.reader();
assert(rd.read_i8() == Option::Some(1), 'expected 1');
assert(
rd.read_i128_le() == Option::Some(0x11100f0e0d0c0b0a0908070605040302), 'not 0x11100f0e0...'
);
assert(
rd.read_u128_le() == Option::Some(0x21201f1e1d1c1b1a1918171615141312), 'not 0x21201f1e1d...'
);
assert(rd.read_i64_le() == Option::Some(0x2928272625242322), 'not 0x29282726...');
assert(
rd.read_u128_le() == Option::Some(0x393837363534333231302f2e2d2c2b2a), 'not 0x3938373635...'
);
assert(rd.read_u32_le() == Option::Some(0x3d3c3b3a), 'not 0x3d3c3b3a');
assert(rd.read_i16_le() == Option::Some(0x3f3e), 'not 0x3f3e');
assert(rd.read_u8() == Option::Some(0x40), 'not 0x40');
assert(rd.read_u8().is_none(), 'expected none');
}

#[test]
#[available_gas(20000000)]
fn test_read_u256() {
Expand All @@ -70,6 +91,16 @@ fn test_read_u256() {
assert(low1 == 0x1112131415161718191a1b1c1d1e1f20_u128, 'wrong value for low1');
}

#[test]
#[available_gas(20000000)]
fn test_read_u256_le() {
let ba = test_byte_array_64();
let mut rd = ba.reader();
let u256{low: low1, high: high1 } = rd.read_u256_le().unwrap();
assert(high1 == 0x201f1e1d1c1b1a191817161514131211_u128, 'wrong value for high1');
assert(low1 == 0x100f0e0d0c0b0a090807060504030201_u128, 'wrong value for low1');
}

#[test]
#[available_gas(20000000)]
fn test_read_u512() {
Expand All @@ -82,3 +113,15 @@ fn test_read_u512() {
assert(limb1 == 0x2122232425262728292a2b2c2d2e2f30_u128, 'wrong value for limb1');
assert(limb0 == 0x3132333435363738393a3b3c3d3e3f40_u128, 'wrong value for limb0');
}

#[test]
#[available_gas(20000000)]
fn test_read_u512_le() {
let ba = test_byte_array_64();
let mut rd = ba.reader();
let u512{limb0, limb1, limb2, limb3 } = rd.read_u512_le().unwrap();
assert(limb0 == 0x100f0e0d0c0b0a090807060504030201_u128, 'wrong value for limb0');
assert(limb1 == 0x201f1e1d1c1b1a191817161514131211_u128, 'wrong value for limb1');
assert(limb2 == 0x302f2e2d2c2b2a292827262524232221_u128, 'wrong value for limb2');
assert(limb3 == 0x403f3e3d3c3b3a393837363534333231_u128, 'wrong value for limb3');
}

0 comments on commit 6434ba2

Please sign in to comment.