Skip to content

Commit 6be5ef1

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 a6ae7c7 commit 6be5ef1

File tree

2 files changed

+66
-3
lines changed

2 files changed

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

655698
pub trait ToByteSpanTrait<C> {

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.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+
// TODO(giladchase): test with slice.
555575
}

0 commit comments

Comments
 (0)