Skip to content

Commit adedbc2

Browse files
author
Gilad Chase
committed
feat(byte_array): add ByteArray::span and Span::len
1 parent 195103b commit adedbc2

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-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: 47 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]
@@ -463,6 +466,50 @@ fn test_from_collect() {
463466
assert_eq!(ba, "hello");
464467
}
465468

469+
#[test]
470+
fn test_span_len() {
471+
// Test simple happy flow --- value is included in the last word.
472+
// TODO(giladchase): add short string test here once supported cast into span.
473+
let ba: ByteArray = "A";
474+
let span = ba.span();
475+
assert_eq(@span.len(), @1, 'wrong span len');
476+
assert!(!span.is_empty());
477+
478+
// Test empty.
479+
let empty_ba: ByteArray = "";
480+
let empty_span = empty_ba.span();
481+
assert_eq(@empty_span.len(), @0, 'empty span len != 0');
482+
assert!(empty_span.is_empty());
483+
484+
// First word in the array + start offset, second in last word.
485+
let two_byte31: ByteArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg";
486+
let mut single_span = two_byte31.span();
487+
// TODO(giladchase): use slice once supported.
488+
single_span.first_char_start_offset = 1;
489+
490+
assert_eq(@single_span.len(), @32, 'len error with start offset');
491+
assert!(!single_span.is_empty());
492+
493+
// First word in the array + start offset, second in the array, third in last word.
494+
let three_bytes31: ByteArray =
495+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$"; // 64 chars.
496+
let mut three_span = three_bytes31.span();
497+
three_span.first_char_start_offset = 1;
498+
assert_eq(@three_span.len(), @63, 'len error with size-3 bytearray');
499+
assert!(!three_span.is_empty());
500+
}
501+
502+
#[test]
503+
fn test_span_copy() {
504+
let ba: ByteArray = "12";
505+
let span = ba.span();
506+
assert_eq(@span.len(), @2, 'wrong span len');
507+
508+
let other_span = span;
509+
assert_eq(@other_span.len(), @2, 'span len is equal to Copy');
510+
assert_eq(@other_span.len(), @span.len(), 'original span still usable');
511+
}
512+
466513
// ========= Test helper functions =========
467514

468515
fn compare_spans<T, +crate::fmt::Debug<T>, +PartialEq<T>, +Copy<T>, +Drop<T>>(

0 commit comments

Comments
 (0)