Skip to content

Commit 4e56cca

Browse files
author
Gilad Chase
committed
feat(byte_array): add at and index to ByteSpan
1 parent 04b050b commit 4e56cca

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

corelib/src/byte_array.cairo

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
//! assert!(first_byte == 0x41);
4343
//! ```
4444

45+
use crate::IndexView;
4546
use crate::array::{ArrayTrait, Span, SpanTrait};
4647
use crate::bytes_31::split_bytes31;
4748
#[allow(unused_imports)]
@@ -691,6 +692,27 @@ pub impl ByteSpanImpl of ByteSpanTrait {
691692
ba
692693
}
693694

695+
fn at(self: @ByteSpan, index: usize) -> Option<u8> {
696+
let actual_index = index.checked_add(upcast(self.first_char_start_offset))?;
697+
let (word_index, index_in_word) = DivRem::div_rem(actual_index, BYTES_IN_BYTES31_NONZERO);
698+
699+
match self.data.get(word_index) {
700+
Some(word) => {
701+
// index_in_word is from MSB, we need index from LSB.
702+
Some(word.at(BYTES_IN_BYTES31 - 1 - index_in_word))
703+
},
704+
None => {
705+
if word_index == self.data.len() && index_in_word < upcast(self.remainder_len) {
706+
// index_in_word is from MSB, we need index from LSB.
707+
let index_in_remainder = upcast(self.remainder_len) - 1 - index_in_word;
708+
Some(u8_at_u256((*self.remainder_word).into(), index_in_remainder))
709+
} else {
710+
None // Out of bounds.
711+
}
712+
},
713+
}
714+
}
715+
694716
/// Returns a slice of the ByteSpan from the given start position with the given length.
695717
fn slice(self: @ByteSpan, start: usize, len: usize) -> Option<ByteSpan> {
696718
if len == 0 {
@@ -732,6 +754,12 @@ impl ByteSpanDefault of Default<ByteSpan> {
732754
}
733755
}
734756

757+
impl ByteSpanIndex of IndexView<ByteSpan, usize, u8> {
758+
fn index(self: @ByteSpan, index: usize) -> u8 {
759+
self.at(index).expect('Index out of bounds')
760+
}
761+
}
762+
735763
/// Trait for types that can be converted into a `ByteSpan`.
736764
pub trait ToByteSpanTrait<C> {
737765
#[must_use]

corelib/src/test/byte_array_test.cairo

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,3 +727,64 @@ fn test_span_multiple_start_offset_slicing() {
727727
let result3: ByteArray = slice3.to_byte_array();
728728
assert_eq!(result3, "def", "third slice");
729729
}
730+
731+
#[test]
732+
fn test_span_at_and_index() {
733+
// Test simple access.
734+
let ba: ByteArray = "AB";
735+
let span = ba.span();
736+
assert_eq!(span.at(0), Some('A'));
737+
assert_eq!(span.at(1), Some('B'));
738+
assert_eq!(span.at(2), None);
739+
740+
// Test index operator on same span.
741+
assert_eq!(span[0], 'A');
742+
assert_eq!(span[1], 'B');
743+
744+
// Test with offset and two words.
745+
let ba_33: ByteArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg";
746+
let mut span = ba_33.span();
747+
span = span.slice(1, 32).unwrap();
748+
assert_eq!(span.at(0), Option::Some('B'));
749+
assert_eq!(span.at(30), Option::Some('f'));
750+
assert_eq!(span.at(31), Option::Some('g'));
751+
assert_eq!(span.at(32), Option::None);
752+
753+
// Test with offset and two words.
754+
// 64 bytes: 31 + 31 + 2.
755+
let ba_64: ByteArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$";
756+
let mut span = ba_64.span();
757+
span = span.slice(1, 63).unwrap();
758+
assert_eq!(span.at(30), Some('f'), "byte 30 - last of 1nd word");
759+
assert_eq!(span.at(31), Some('g'), "byte 31 - first of 2nd word");
760+
assert_eq!(span.at(60), Some('9'), "byte 60 - last of 2nd word");
761+
assert_eq!(span.at(61), Some('#'), "byte 61 - first in last_word");
762+
assert_eq!(span.at(62), Some('$'), "byte 62 - last in last_word");
763+
assert_eq!(span.at(63), None);
764+
765+
// Test empty span.
766+
let empty: ByteArray = Default::default();
767+
let empty_span = empty.span();
768+
assert_eq!(empty_span.at(0), None);
769+
}
770+
771+
#[test]
772+
#[should_panic(expected: ('Index out of bounds',))]
773+
fn test_span_index_out_of_bounds() {
774+
let ba: ByteArray = "AB";
775+
let span = ba.span();
776+
let _x = span[2]; // Should panic
777+
}
778+
779+
#[test]
780+
fn test_span_at_overflows() {
781+
// Test overflow protection with large indices.
782+
let ba: ByteArray = "test";
783+
let span = ba.span();
784+
785+
assert_eq!(span.at(Bounded::<usize>::MAX), None);
786+
787+
let sliced = ba.span().slice(1, 3).unwrap();
788+
assert_eq!(sliced.at(Bounded::<usize>::MAX - 1), None);
789+
assert_eq!(sliced.at(Bounded::<usize>::MAX), None);
790+
}

0 commit comments

Comments
 (0)