Skip to content

Commit 9c5f473

Browse files
author
Gilad Chase
committed
feat(byte_array): add ByteSpan::into and bytes31_slice
When slicing bytespans (to-be-implemented), the end-offset is trimmed by shifting the word to the right. The left offset, however, will be trimmed lazily only if the `ByteSpan` is casted into a `ByteArray`. Note: Lazily removing the end-offset will require saving an additional field, `end_offset` in `ByteSpan`, due to how strings are represented inside felt252 (the first byte is the msb of the word). In other words, we cannot just reduce the remainder_len, because then it'd be impossible to know how much to trim off from the remainder word at `into`.
1 parent e89a587 commit 9c5f473

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

corelib/src/byte_array.cairo

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
//! ```
4444

4545
use crate::array::{ArrayTrait, Span, SpanTrait};
46+
use crate::bytes_31::split_bytes31;
4647
#[allow(unused_imports)]
4748
use crate::bytes_31::{
4849
BYTES_IN_BYTES31, Bytes31Trait, POW_2_128, POW_2_8, U128IntoBytes31, U8IntoBytes31,
@@ -652,6 +653,14 @@ pub impl ByteArraySpanImpl of ByteSpanTrait {
652653
}
653654
}
654655

656+
impl ByteSpanDefault of Default<ByteSpan> {
657+
fn default() -> ByteSpan {
658+
ByteSpan {
659+
data: [].span(), first_char_start_offset: 0, remainder_word: 0, remainder_len: 0,
660+
}
661+
}
662+
}
663+
655664
pub trait ToByteSpanTrait<C> {
656665
#[must_use]
657666
fn span(self: @C) -> ByteSpan;
@@ -673,3 +682,39 @@ impl ByteSpanToByteSpan of ToByteSpanTrait<ByteSpan> {
673682
*self
674683
}
675684
}
685+
686+
impl ByteSpanIntoByteArray of Into<ByteSpan, ByteArray> {
687+
/// Converts a `ByteSpan` into a `ByteArray`.
688+
/// The cast includes trimming the start_offset of the first word of the span (which is created
689+
/// when slicing).
690+
///
691+
/// Note: creating `ByteArray.data` from `Span` requires allocating a new memory
692+
/// segment for the returned array, and *O*(*n*) operations to populate the array with the
693+
/// content of the span (see also `SpanIntoArray`).
694+
fn into(mut self: ByteSpan) -> ByteArray {
695+
let remainder_len = upcast(self.remainder_len);
696+
let Some(first_word) = self.data.pop_front() else {
697+
// Slice is included entirely in the remainder word.
698+
let len_without_offset: usize = remainder_len - upcast(self.first_char_start_offset);
699+
let (start_offset_trimmed, _) = split_bytes31(
700+
self.remainder_word, remainder_len, len_without_offset,
701+
);
702+
return ByteArray {
703+
data: array![],
704+
pending_word: start_offset_trimmed,
705+
pending_word_len: upcast(len_without_offset),
706+
};
707+
};
708+
709+
let mut ba = Default::default();
710+
let n_bytes_to_append = BYTES_IN_BYTES31 - upcast(self.first_char_start_offset);
711+
let (first_word_no_offset, _) = split_bytes31(
712+
(*first_word).into(), BYTES_IN_BYTES31, n_bytes_to_append,
713+
);
714+
ba.append_word(first_word_no_offset, n_bytes_to_append);
715+
716+
ba.data.append_span(self.data); // Contains everything after the first word.
717+
ba.append_word(self.remainder_word, remainder_len);
718+
ba
719+
}
720+
}

corelib/src/test/byte_array_test.cairo

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,29 @@ fn test_span_copy() {
547547

548548
let other_span = span;
549549
assert_eq!(other_span.len(), 2);
550+
assert_eq!(ba, span.into());
551+
550552
let span_again = span.span();
551-
assert_eq!(span_again.len(), span.len());
553+
assert_eq!(ba, span_again.into());
554+
assert_eq!(ba, span.into());
555+
552556
let even_more_span_again = other_span.span();
553-
assert_eq!(even_more_span_again.len(), other_span.len());
554-
// TODO(giladchase): verify span content once we add `into` or `PartialEq`.
557+
assert_eq!(ba, even_more_span_again.into());
558+
assert_eq!(ba, other_span.into());
559+
assert_eq!(ba, span.into());
560+
}
561+
562+
#[test]
563+
fn test_span_into_bytearray() {
564+
let empty_ba: ByteArray = "";
565+
assert_eq!(empty_ba.span().into(), empty_ba);
566+
567+
// Only remainder.
568+
let small_ba: ByteArray = "hello";
569+
assert_eq!(small_ba.span().into(), small_ba);
570+
571+
// Data word and remainder.
572+
let large_ba: ByteArray = "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW"; // 40 bytes
573+
assert_eq!(large_ba.span().into(), large_ba);
574+
// TODO(giladchase): test with slice.
555575
}

0 commit comments

Comments
 (0)