Skip to content

Commit 1584b10

Browse files
author
Gilad Chase
committed
feat(byte_array): add ByteArray::span and Span::len
1 parent 07a2911 commit 1584b10

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

corelib/src/byte_array.cairo

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

45-
use crate::array::{ArrayTrait, SpanTrait};
45+
use crate::array::{ArrayTrait, Span as ArraySpan, SpanTrait as ArraySpanTrait};
4646
#[allow(unused_imports)]
4747
use crate::bytes_31::{
4848
BYTES_IN_BYTES31, Bytes31Trait, POW_2_128, POW_2_8, U128IntoBytes31, U8IntoBytes31,
@@ -585,3 +585,49 @@ impl ByteArrayFromIterator of crate::iter::FromIterator<ByteArray, u8> {
585585
ba
586586
}
587587
}
588+
589+
/// A view into a contiguous collection of a string type.
590+
/// Currently implemented only for `ByteArray`, but will soon be implemented for other string types.
591+
/// `Span` implements the `Copy` and the `Drop` traits.
592+
#[derive(Copy, Drop)]
593+
pub struct Span {
594+
pub(crate) raw_data: ArraySpan<bytes31>,
595+
/// The offset of the first character in the first entry of [Self::raw_data], for use in span
596+
/// slices.
597+
/// Value should be in the range [0, 30].
598+
pub(crate) first_char_start_offset: usize,
599+
/// The last word in the span will always be located in this field, to account for bytearrays
600+
/// which don't include the pending bytes31 word in the array itself.
601+
pub(crate) last_word: felt252,
602+
/// The offset of the last character in the last entry of span, located in [Self::last_word].
603+
/// Value should be in the range [0, 30].
604+
pub(crate) last_char_end_offset: usize,
605+
}
606+
607+
#[generate_trait]
608+
pub impl ByteArraySpanImpl of SpanTrait {
609+
fn len(self: @Span) -> usize {
610+
let raw_data_bytes = self.raw_data.len() * BYTES_IN_BYTES31;
611+
raw_data_bytes - *self.first_char_start_offset + *self.last_char_end_offset
612+
}
613+
614+
fn is_empty(self: @Span) -> bool {
615+
self.len() == 0
616+
}
617+
}
618+
619+
pub trait ToSpanTrait<C> {
620+
#[must_use]
621+
fn span(self: @C) -> Span;
622+
}
623+
624+
impl ByteArrayToSpan of ToSpanTrait<ByteArray> {
625+
fn span(self: @ByteArray) -> Span {
626+
Span {
627+
raw_data: self.data.span(),
628+
first_char_start_offset: 0,
629+
last_word: *self.pending_word,
630+
last_char_end_offset: *self.pending_word_len,
631+
}
632+
}
633+
}

corelib/src/test/byte_array_test.cairo

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
pub use crate::byte_array::{
2+
Span as ByteArraySpan, SpanTrait as ByteArraySpanTrait, ToSpanTrait as ByteArrayToSpanTrait,
3+
};
14
use crate::test::test_utils::{assert_eq, assert_ne};
25

36
// ========= Test-utils =========
@@ -93,6 +96,14 @@ fn test_byte_array_33() -> ByteArray {
9396
ba2
9497
}
9598

99+
fn test_byte_array_64() -> ByteArray {
100+
let mut ba3 = Default::default();
101+
ba3.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 31);
102+
ba3.append_word(0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e, 31);
103+
ba3.append_word(0x3f40, 2);
104+
ba3
105+
}
106+
96107
// ========= Tests =========
97108

98109
#[test]
@@ -597,3 +608,47 @@ fn test_from_collect() {
597608
let ba: ByteArray = array!['h', 'e', 'l', 'l', 'o'].into_iter().collect();
598609
assert_eq!(ba, "hello");
599610
}
611+
612+
613+
#[test]
614+
fn test_span_len() {
615+
// Test simple happy flow --- value is included in the last word.
616+
// TODO(giladchase): add short string test here once supported cast into span.
617+
let ba = test_byte_array_1();
618+
let span = ba.span();
619+
assert_eq(@span.len(), @1, 'wrong span len');
620+
assert!(!span.is_empty());
621+
622+
// Test empty.
623+
let empty_ba: ByteArray = Default::default();
624+
let empty_span = empty_ba.span();
625+
assert_eq(@empty_span.len(), @0, 'empty span len != 0');
626+
assert!(empty_span.is_empty());
627+
628+
// First word in the array + start offset, second in last word.
629+
let two_byte31 = test_byte_array_33();
630+
let mut single_span = two_byte31.span();
631+
// TODO(giladchase): use slice once supported.
632+
single_span.first_char_start_offset = 1;
633+
634+
assert_eq(@single_span.len(), @32, 'len error with start offset');
635+
assert!(!single_span.is_empty());
636+
637+
// First word in the array + start offset, second in the array, third in last word.
638+
let three_bytes31 = test_byte_array_64();
639+
let mut three_span = three_bytes31.span();
640+
three_span.first_char_start_offset = 1;
641+
assert_eq(@three_span.len(), @63, 'len error with size-3 bytearray');
642+
assert!(!three_span.is_empty());
643+
}
644+
645+
#[test]
646+
fn test_span_copy() {
647+
let ba = test_byte_array_2();
648+
let span = ba.span();
649+
assert_eq(@span.len(), @2, 'wrong span len');
650+
651+
let other_span = span;
652+
assert_eq(@other_span.len(), @2, 'span len is equal to Copy');
653+
assert_eq(@other_span.len(), @span.len(), 'original span still usable');
654+
}

0 commit comments

Comments
 (0)