admin管理员组文章数量:1277380
I am trying to implement deserialization into Rc<[u8]>
without double allocating. The solutions I saw always creates an intermediate Vec<u8>
and filling it in, and then converting it to an Rc
. But this causes double allocation, first when filling in the Vec<u8>
, and second converting it into Rc<[u8]>
.
Is there a way to directly allocate N bytes (N is read from the deserialization reader) into Rc<[u8]>
, and using the buffer as input to the reader? I am OK to unsafe usage as long as there are no memory leaks.
I am trying to implement deserialization into Rc<[u8]>
without double allocating. The solutions I saw always creates an intermediate Vec<u8>
and filling it in, and then converting it to an Rc
. But this causes double allocation, first when filling in the Vec<u8>
, and second converting it into Rc<[u8]>
.
Is there a way to directly allocate N bytes (N is read from the deserialization reader) into Rc<[u8]>
, and using the buffer as input to the reader? I am OK to unsafe usage as long as there are no memory leaks.
2 Answers
Reset to default 9This can be accomplished in safe code, by using Rc::from_iter
with a TrustedLen
iterator:
Iterators of known length
When your
Iterator
implementsTrustedLen
and is of an exact size, a single allocation will be made for theRc<[T]>
. For example:
use std::rc::Rc;
pub fn rc_slice<T: Default>(n: usize) -> Rc<[T]> {
Rc::from_iter((0..n).map(|_| T::default()))
}
fn main() {
let mut data = rc_slice::<u8>(16);
// TODO: ovewrite contents of `data` with deserialized data
*Rc::get_mut(&mut data).unwrap()[0] = ...;
}
Note: This solution relies on a zero/Default
initialization of the Rc
contents prior to deserialization, which works for u8
. If this isn't available for your type, consider using unsafe
to allocate an uninitialized Rc
-slice.
Finn Bear's answer is good and provides a way to allocate exactly once, however it has a problem. TrustedLen
is currently a nightly-only trait. So if you have a custom deserialisation logic, that cannot be expressed with iterators from standard library already implementing TrustedLen
, then this specialisation will not work, and compiler will still use the default "collect into a Vec
, and then reallocate" strategy.
However, there is another way. Since Rust 1.82.0 you can use Rc::new_uninit_slice
to allocate Rc<[MaybeUnint<T>]>
, then get a mutable reference to this slice with Rc::get_mut
, with which you can initialise elements one by one, and finally after all elements has been initialised, you can call Rc::assume_init
(which is obviously unsafe), to convert it into Rc<[T]>
.
You have to be very careful with handling partially initialised data and do a proper cleanup if you encounter a panic mid-initialisation, but a simple "guard" struct with proper Drop
implementation will do that for you.
I don't know how you perform deserialisation, so as an example I'll just paste code snipped from the documentation of Rc::assume_init
:
let mut values = Rc::<[u32]>::new_uninit_slice(3);
// Deferred initialization:
let data = Rc::get_mut(&mut values).unwrap();
data[0].write(1);
data[1].write(2);
data[2].write(3);
// SAFETY: all values have been initialised
let values = unsafe { values.assume_init() };
assert_eq!(*values, [1, 2, 3])
本文标签: rustInitializing Rcltu8gt directly without double allocationStack Overflow
版权声明:本文标题:rust - Initializing Rc<[u8]> directly without double allocation - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741286888a2370324.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论