Skip to content

Commit b769bea

Browse files
author
Gilad Chase
committed
feat(byte_array): add ByteSpan::to_byte_array
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 `to_byte_array`.
1 parent b885e88 commit b769bea

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

corelib/src/byte_array.cairo

Lines changed: 43 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,
@@ -655,6 +656,48 @@ pub impl ByteSpanImpl of ByteSpanTrait {
655656
fn is_empty(self: ByteSpan) -> bool {
656657
self.remainder_len == 0 && self.data.len() == 0
657658
}
659+
660+
/// Converts a `ByteSpan` into a `ByteArray`.
661+
/// The cast includes trimming the start_offset of the first word of the span (which is created
662+
/// when slicing).
663+
///
664+
/// Note: creating `ByteArray.data` from `Span` requires allocating a new memory
665+
/// segment for the returned array, and *O*(*n*) operations to populate the array with the
666+
/// content of the span (see also `SpanIntoArray`).
667+
fn to_byte_array(mut self: ByteSpan) -> ByteArray {
668+
let remainder_len = upcast(self.remainder_len);
669+
let Some(first_word) = self.data.pop_front() else {
670+
// Slice is included entirely in the remainder word.
671+
let len_without_offset: usize = remainder_len - upcast(self.first_char_start_offset);
672+
let (start_offset_trimmed, _) = split_bytes31(
673+
self.remainder_word, remainder_len, len_without_offset,
674+
);
675+
return ByteArray {
676+
data: array![],
677+
pending_word: start_offset_trimmed,
678+
pending_word_len: upcast(len_without_offset),
679+
};
680+
};
681+
682+
let mut ba = Default::default();
683+
let n_bytes_to_append = BYTES_IN_BYTES31 - upcast(self.first_char_start_offset);
684+
let (first_word_no_offset, _) = split_bytes31(
685+
(*first_word).into(), BYTES_IN_BYTES31, n_bytes_to_append,
686+
);
687+
ba.append_word(first_word_no_offset, n_bytes_to_append);
688+
689+
// Append the rest of the span, which is now aligned since the first word was popped.
690+
ba.append_aligned_byte_span(self);
691+
ba
692+
}
693+
}
694+
695+
impl ByteSpanDefault of Default<ByteSpan> {
696+
fn default() -> ByteSpan {
697+
ByteSpan {
698+
data: [].span(), first_char_start_offset: 0, remainder_word: 0, remainder_len: 0,
699+
}
700+
}
658701
}
659702

660703
/// Trait for types that can be converted into a `ByteSpan`.

corelib/src/test/byte_array_test.cairo

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

548548
let other_span = span;
549549
assert_eq!(other_span.len(), 2);
550+
assert_eq!(ba, span.to_byte_array());
551+
550552
let span_again = span.span();
551-
assert_eq!(span_again.len(), span.len());
553+
assert_eq!(ba, span_again.to_byte_array());
554+
assert_eq!(ba, span.to_byte_array());
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.to_byte_array());
558+
assert_eq!(ba, other_span.to_byte_array());
559+
assert_eq!(ba, span.to_byte_array());
560+
}
561+
562+
#[test]
563+
fn test_span_to_bytearray() {
564+
let empty_ba: ByteArray = "";
565+
assert_eq!(empty_ba.span().to_byte_array(), empty_ba);
566+
567+
// Only remainder.
568+
let small_ba: ByteArray = "hello";
569+
assert_eq!(small_ba.span().to_byte_array(), small_ba);
570+
571+
// Data word and remainder.
572+
let large_ba: ByteArray = "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW"; // 40 bytes
573+
assert_eq!(large_ba.span().to_byte_array(), large_ba);
574+
575+
// Two data words and remainder.
576+
let even_larger_ba: ByteArray =
577+
"abcdeFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$"; // 64 bytes
578+
assert_eq!(even_larger_ba.span().to_byte_array(), even_larger_ba);
579+
// TODO(giladchase): test with slice.
555580
}

0 commit comments

Comments
 (0)