Skip to content

Commit 2d5edca

Browse files
author
Gilad Chase
committed
feat(byte_array): add ByteArray::span and Span::len
1 parent a2f209e commit 2d5edca

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 =========
@@ -63,6 +66,14 @@ fn test_byte_array_33() -> ByteArray {
6366
ba2
6467
}
6568

69+
fn test_byte_array_64() -> ByteArray {
70+
let mut ba3 = Default::default();
71+
ba3.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 31);
72+
ba3.append_word(0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e, 31);
73+
ba3.append_word(0x3f40, 2);
74+
ba3
75+
}
76+
6677
// ========= Tests =========
6778

6879
#[test]
@@ -527,3 +538,47 @@ fn test_from_collect() {
527538
let ba: ByteArray = array!['h', 'e', 'l', 'l', 'o'].into_iter().collect();
528539
assert_eq!(ba, "hello");
529540
}
541+
542+
543+
#[test]
544+
fn test_span_len() {
545+
// Test simple happy flow --- value is included in the last word.
546+
// TODO(giladchase): add short string test here once supported cast into span.
547+
let ba = test_byte_array_1();
548+
let span = ba.span();
549+
assert_eq(@span.len(), @1, 'wrong span len');
550+
assert!(!span.is_empty());
551+
552+
// Test empty.
553+
let empty_ba: ByteArray = Default::default();
554+
let empty_span = empty_ba.span();
555+
assert_eq(@empty_span.len(), @0, 'empty span len != 0');
556+
assert!(empty_span.is_empty());
557+
558+
// First word in the array + start offset, second in last word.
559+
let two_byte31 = test_byte_array_33();
560+
let mut single_span = two_byte31.span();
561+
// TODO(giladchase): use slice once supported.
562+
single_span.first_char_start_offset = 1;
563+
564+
assert_eq(@single_span.len(), @32, 'len error with start offset');
565+
assert!(!single_span.is_empty());
566+
567+
// First word in the array + start offset, second in the array, third in last word.
568+
let three_bytes31 = test_byte_array_64();
569+
let mut three_span = three_bytes31.span();
570+
three_span.first_char_start_offset = 1;
571+
assert_eq(@three_span.len(), @63, 'len error with size-3 bytearray');
572+
assert!(!three_span.is_empty());
573+
}
574+
575+
#[test]
576+
fn test_span_copy() {
577+
let ba = test_byte_array_2();
578+
let span = ba.span();
579+
assert_eq(@span.len(), @2, 'wrong span len');
580+
581+
let other_span = span;
582+
assert_eq(@other_span.len(), @2, 'span len is equal to Copy');
583+
assert_eq(@other_span.len(), @span.len(), 'original span still usable');
584+
}

0 commit comments

Comments
 (0)