Skip to content

Commit 04b763d

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

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-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)]
@@ -653,6 +654,27 @@ pub impl ByteArraySpanImpl of ByteSpanTrait {
653654
self.data.len() == 0 && *self.remainder_len == 0
654655
}
655656

657+
fn at(self: @ByteSpan, index: usize) -> Option<u8> {
658+
let actual_index = index.checked_add(upcast(self.first_char_start_offset))?;
659+
let (word_index, index_in_word) = DivRem::div_rem(actual_index, BYTES_IN_BYTES31_NONZERO);
660+
661+
match self.data.get(word_index) {
662+
Some(word) => {
663+
// index_in_word is from MSB, we need index from LSB.
664+
Some(word.at(BYTES_IN_BYTES31 - 1 - index_in_word))
665+
},
666+
None => {
667+
if word_index == self.data.len() && index_in_word < upcast(self.remainder_len) {
668+
// index_in_word is from MSB, we need index from LSB.
669+
let index_in_remainder = upcast(self.remainder_len) - 1 - index_in_word;
670+
Some(u8_at_u256((*self.remainder_word).into(), index_in_remainder))
671+
} else {
672+
None // Out of bounds.
673+
}
674+
},
675+
}
676+
}
677+
656678
/// Returns a slice of the ByteSpan from the given start position with the given length.
657679
fn slice(self: @ByteSpan, start: usize, len: usize) -> Option<ByteSpan> {
658680
if len == 0 {
@@ -694,6 +716,12 @@ impl ByteSpanDefault of Default<ByteSpan> {
694716
}
695717
}
696718

719+
impl ByteSpanIndex of IndexView<ByteSpan, usize, u8> {
720+
fn index(self: @ByteSpan, index: usize) -> u8 {
721+
self.at(index).expect('Index out of bounds')
722+
}
723+
}
724+
697725
pub trait ToByteSpanTrait<C> {
698726
#[must_use]
699727
fn span(self: @C) -> ByteSpan;

corelib/src/test/byte_array_test.cairo

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,3 +726,63 @@ fn test_span_multiple_start_offset_slicing() {
726726
assert_eq!(result3, "def", "third slice");
727727
}
728728

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

0 commit comments

Comments
 (0)