Skip to content

Commit 1fb81cc

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 1ec28a3 commit 1fb81cc

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

662705
/// 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
@@ -549,9 +549,34 @@ fn test_span_copy() {
549549

550550
let other_span = span;
551551
assert_eq!(other_span.len(), 2);
552+
assert_eq!(ba, span.to_byte_array());
553+
552554
let span_again = span.span();
553-
assert_eq!(span_again.len(), span.len());
555+
assert_eq!(ba, span_again.to_byte_array());
556+
assert_eq!(ba, span.to_byte_array());
557+
554558
let even_more_span_again = other_span.span();
555-
assert_eq!(even_more_span_again.len(), other_span.len());
556-
// TODO(giladchase): verify span content once we add `into` or `PartialEq`.
559+
assert_eq!(ba, even_more_span_again.to_byte_array());
560+
assert_eq!(ba, other_span.to_byte_array());
561+
assert_eq!(ba, span.to_byte_array());
562+
}
563+
564+
#[test]
565+
fn test_span_to_bytearray() {
566+
let empty_ba: ByteArray = "";
567+
assert_eq!(empty_ba.span().to_byte_array(), empty_ba);
568+
569+
// Only remainder.
570+
let small_ba: ByteArray = "hello";
571+
assert_eq!(small_ba.span().to_byte_array(), small_ba);
572+
573+
// Data word and remainder.
574+
let large_ba: ByteArray = "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW"; // 40 bytes
575+
assert_eq!(large_ba.span().to_byte_array(), large_ba);
576+
577+
// Two data words and remainder.
578+
let even_larger_ba: ByteArray =
579+
"abcdeFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$"; // 64 bytes
580+
assert_eq!(even_larger_ba.span().to_byte_array(), even_larger_ba);
581+
// TODO(giladchase): test with slice.
557582
}

0 commit comments

Comments
 (0)