Skip to content

Commit c694e93

Browse files
committed
Optimize dynamic std types
1 parent bd7c483 commit c694e93

24 files changed

Lines changed: 1229 additions & 411 deletions

File tree

docs/book/src/reference/undefined_behavior.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ reserve the right to make some of the listed behavior defined in the future.
1414
* Reading and writing `raw_ptr` and `raw_slice`.
1515
* Slicing and indexing out of bounds by directly using compiler intrinsics.
1616
* Modifying collections while iterating over them using `Iterator`s.
17+
* Accessing a dynamic type instance after it has been moved by a `from_moved_...` constructor.
18+
* Passing a `raw_slice` that doesn't point to heap memory to a `from_moved_raw_slice` constructor.

sway-lib-std/src/bytes.sw

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,55 @@ impl Bytes {
170170
}
171171
}
172172

173+
/// Constructs a new `Bytes` that takes the ownership of the `slice`.
174+
///
175+
/// # Additional Information
176+
///
177+
/// `slice` **must point to a heap-allocated memory**.
178+
/// `slice`, or its owner, like, e.g., `Vec`, **must not be used
179+
/// after the ownership is transferred to the newly created `Bytes`**.
180+
///
181+
/// Violating the above restrictions results in an undefined behavior.
182+
///
183+
/// To create a new `Bytes` from a `raw_slice` that copies the slice content
184+
/// and does not take the ownership, use `Bytes::from(raw_slice)`.
185+
///
186+
/// # Arguments
187+
///
188+
/// * `slice`: [raw_slice] - The heap-allocated slice whose ownership is transferred to the `Bytes`.
189+
///
190+
/// # Returns
191+
///
192+
/// * [Bytes] - A new `Bytes` whose content is the original content of the `slice`.
193+
///
194+
/// # Examples
195+
///
196+
/// ```sway
197+
/// use std::bytes::Bytes;
198+
/// use std::vec::Vec;
199+
///
200+
/// fn foo() {
201+
/// let mut source = Vec::<u8>::new();
202+
/// source.push(1u8);
203+
///
204+
/// let bytes = Bytes::from_moved_raw_slice(source.as_raw_slice());
205+
///
206+
/// // ** `source` must not be used after this point. **
207+
///
208+
/// assert_eq(bytes.get(0).unwrap(), 1u8);
209+
/// }
210+
/// ```
211+
pub fn from_moved_raw_slice(slice: raw_slice) -> Self {
212+
let len_and_capacity = slice.number_of_bytes();
213+
Self {
214+
buf: RawBytes {
215+
ptr: slice.ptr(),
216+
cap: len_and_capacity,
217+
},
218+
len: len_and_capacity,
219+
}
220+
}
221+
173222
/// Appends an element to the back of a `Bytes` collection.
174223
///
175224
/// # Arguments
@@ -541,6 +590,9 @@ impl Bytes {
541590

542591
/// Clears the `Bytes`, removing all values.
543592
///
593+
/// Note that this method has no effect on the allocated capacity
594+
/// of the `Bytes`.
595+
///
544596
/// # Examples
545597
///
546598
/// ```sway
@@ -554,7 +606,6 @@ impl Bytes {
554606
/// }
555607
/// ```
556608
pub fn clear(ref mut self) {
557-
self.buf = RawBytes::new();
558609
self.len = 0;
559610
}
560611

@@ -752,11 +803,12 @@ impl Bytes {
752803

753804
// reallocate with combined capacity, write `slice`, set buffer capacity
754805
if self.buf.cap < both_len {
755-
let new_slice = raw_slice::from_parts::<u8>(
756-
realloc_bytes(self.buf.ptr, self.buf.cap, both_len),
757-
both_len,
758-
);
759-
self.buf = RawBytes::from(new_slice);
806+
// `realloc_bytes` already returns a fresh buffer that owns the
807+
// existing content, so we take its ownership directly into `RawBytes`
808+
self.buf = RawBytes {
809+
ptr: realloc_bytes(self.buf.ptr, self.buf.cap, both_len),
810+
cap: both_len,
811+
};
760812
}
761813

762814
let new_ptr = self.buf.ptr.add_uint_offset(other_start);
@@ -1104,7 +1156,15 @@ impl TryInto<b256> for Bytes {
11041156
impl From<raw_slice> for Bytes {
11051157
/// Creates a `Bytes` from a `raw_slice`.
11061158
///
1107-
/// ### Examples
1159+
/// # Additional Information
1160+
///
1161+
/// The content of the `slice` gets copied to a newly created `Bytes`
1162+
/// that allocates its own buffer.
1163+
///
1164+
/// To take the ownership of the `slice` and move it to the newly
1165+
/// created `Bytes` without copying the content, use `Bytes::from_moved_raw_slice`.
1166+
///
1167+
/// # Examples
11081168
///
11091169
/// ```sway
11101170
/// use std:bytes::Bytes;
@@ -1121,10 +1181,10 @@ impl From<raw_slice> for Bytes {
11211181
/// let vec_as_raw_slice = vec.as_raw_slice();
11221182
/// let bytes = Bytes::from(vec_as_raw_slice);
11231183
///
1124-
/// assert(bytes.len == 3);
1125-
/// assert(bytes.get(0).unwrap() == a);
1126-
/// assert(bytes.get(1).unwrap() == b);
1127-
/// assert(bytes.get(2).unwrap() == c);
1184+
/// assert_eq(bytes.len, 3);
1185+
/// assert_eq(bytes.get(0).unwrap(), a);
1186+
/// assert_eq(bytes.get(1).unwrap(), b);
1187+
/// assert_eq(bytes.get(2).unwrap(), c);
11281188
/// ```
11291189
fn from(slice: raw_slice) -> Self {
11301190
Self {

sway-lib-std/src/codec.sw

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1769,7 +1769,7 @@ where
17691769
};
17701770
*item
17711771
} else {
1772-
let mut buffer = BufferReader::from_parts(data.ptr(), data.len::<u8>());
1772+
let mut buffer = BufferReader::from_parts(data.ptr(), data.number_of_bytes());
17731773
T::abi_decode(buffer)
17741774
}
17751775
}

sway-lib-std/src/hash.sw

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,13 +451,33 @@ where
451451

452452
/// Returns the `Hasher`'s initial capacity optimal for hashing
453453
/// an instance of type `T`.
454+
#[cfg(experimental_new_hashing = false)]
454455
fn get_initial_capacity<T>() -> u64 {
455456
if __is_str_array::<T>() {
456457
__size_of_str_array::<T>()
457458
} else {
458-
// This will work accurately for all non-heap types.
459-
// For heap types, it still gives a slightly better
460-
// start then having the empty buffer.
459+
// This will work accurately for all non-dynamic types.
460+
// For dynamic types, it might give a slightly better
461+
// start then having an empty buffer, or a useless
462+
// initial allocation, depending on the size of the
463+
// content.
464+
__size_of::<T>()
465+
}
466+
}
467+
468+
/// Returns the `Hasher`'s initial capacity optimal for hashing
469+
/// an instance of type `T`.
470+
#[cfg(experimental_new_hashing = true)]
471+
fn get_initial_capacity<T>() -> u64 {
472+
if __is_str_array::<T>() {
473+
// Add 8 bytes for the length prefix.
474+
__size_of_str_array::<T>() + 8
475+
} else {
476+
// This will work accurately for all non-dynamic types.
477+
// For dynamic types, it might give a slightly better
478+
// start then having an empty buffer, or a useless
479+
// initial allocation, depending on the size of the
480+
// content.
461481
__size_of::<T>()
462482
}
463483
}

sway-lib-std/src/raw_slice.sw

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,16 @@ impl raw_slice {
143143
into_parts(self).0
144144
}
145145

146-
/// Returns the number of elements in the slice.
146+
/// Returns the number of elements in the slice, where each element has a `__size_of::<T>`.
147+
///
148+
/// # Additional information
149+
///
150+
/// If the slice size in bytes is not a multiple of `__size_of::<T>`, the return length
151+
/// is the maximum number of elements of type `T` that can fit into the slice.
147152
///
148153
/// # Returns
149154
///
150-
/// * [u64] - The length of the slice based on `size_of::<T>`.
155+
/// * [u64] - The length of the slice based on `__size_of::<T>`.
151156
///
152157
/// # Examples
153158
///
@@ -165,9 +170,9 @@ impl raw_slice {
165170
///
166171
/// * When `T` is a zero-sized type.
167172
pub fn len<T>(self) -> u64 {
168-
let len = __size_of::<T>();
169-
if len != 0 {
170-
into_parts(self).1 / len
173+
const SIZE_OF_T: u64 = __size_of::<T>();
174+
if SIZE_OF_T != 0 {
175+
into_parts(self).1 / SIZE_OF_T
171176
} else {
172177
__revert(REVERT_WITH_RAW_SLICE_LEN_ZST);
173178
}

sway-lib-std/src/storage/storage_bytes.sw

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ impl StorableSlice<Bytes> for StorageKey<StorageBytes> {
8686
fn read_slice(self) -> Option<Bytes> {
8787
match read_slice_quads(self.field_id()) {
8888
Some(slice) => {
89-
Some(Bytes::from(slice))
89+
// `read_slice_quads` returns a freshly heap-allocated slice that
90+
// is used only here, so we take its ownership instead of copying.
91+
Some(Bytes::from_moved_raw_slice(slice))
9092
},
9193
None => None,
9294
}
@@ -238,7 +240,9 @@ impl StorableSlice<Bytes> for StorageKey<StorageBytes> {
238240
fn read_slice(self) -> Option<Bytes> {
239241
match read_slice_slot(self.field_id()) {
240242
Some(slice) => {
241-
Some(Bytes::from(slice))
243+
// `read_slice_slot` returns a freshly heap-allocated slice that
244+
// is used only here, so we take its ownership instead of copying.
245+
Some(Bytes::from_moved_raw_slice(slice))
242246
},
243247
None => None,
244248
}

sway-lib-std/src/storage/storage_string.sw

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ impl StorableSlice<String> for StorageKey<StorageString> {
8282
fn read_slice(self) -> Option<String> {
8383
match read_slice_quads(self.field_id()) {
8484
Some(slice) => {
85-
Some(String::from(slice))
85+
// `read_slice_quads` returns a freshly heap-allocated slice that
86+
// is used only here, so we take its ownership instead of copying.
87+
Some(String::from_moved_raw_slice(slice))
8688
},
8789
None => None,
8890
}
@@ -223,7 +225,9 @@ impl StorableSlice<String> for StorageKey<StorageString> {
223225
fn read_slice(self) -> Option<String> {
224226
match read_slice_slot(self.field_id()) {
225227
Some(slice) => {
226-
Some(String::from(slice))
228+
// `read_slice_slot` returns a freshly heap-allocated slice that
229+
// is used only here, so we take its ownership instead of copying.
230+
Some(String::from_moved_raw_slice(slice))
227231
},
228232
None => None,
229233
}

sway-lib-std/src/storage/storage_vec.sw

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,9 +1071,9 @@ impl<V> StorageKey<StorageVec<V>> {
10711071
i += 1;
10721072
}
10731073

1074-
Vec::from(__transmute::<(raw_ptr, u64), raw_slice>((new_vec, len_bytes)))
1074+
Vec::from_moved_raw_slice(__transmute::<(raw_ptr, u64), raw_slice>((new_vec, len_bytes)))
10751075
} else {
1076-
Vec::from(__transmute::<(raw_ptr, u64), raw_slice>((ptr, bytes)))
1076+
Vec::from_moved_raw_slice(__transmute::<(raw_ptr, u64), raw_slice>((ptr, bytes)))
10771077
}
10781078
}
10791079
}
@@ -2862,7 +2862,7 @@ impl<V> StorageKey<StorageVec<V>> {
28622862
}
28632863
}
28642864

2865-
Vec::from(raw_slice::from_parts::<V>(elements_ptr, len))
2865+
Vec::from_moved_raw_slice(raw_slice::from_parts::<V>(elements_ptr, len))
28662866
}
28672867
}
28682868

0 commit comments

Comments
 (0)