Skip to content

Commit 2b36019

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

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-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, SpanTrait};
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 ByteSpan {
594+
pub(crate) raw_data: Span<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 ByteSpanTrait {
609+
fn len(self: @ByteSpan) -> 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: @ByteSpan) -> bool {
615+
self.len() == 0
616+
}
617+
}
618+
619+
pub trait ToByteSpanTrait<C> {
620+
#[must_use]
621+
fn span(self: @C) -> ByteSpan;
622+
}
623+
624+
impl ByteArrayToByteSpan of ToByteSpanTrait<ByteArray> {
625+
fn span(self: @ByteArray) -> ByteSpan {
626+
ByteSpan {
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: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub use crate::byte_array::{ByteSpan, ByteSpanTrait, ToByteSpanTrait};
12
use crate::test::test_utils::{assert_eq, assert_ne};
23

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

0 commit comments

Comments
 (0)