oc_wasm_safe/extref.rs
1//! Out-of-line byte-array and string reference types for more efficient CBOR encoding.
2//!
3//! This module defines two reference types, one for byte arrays and one for strings. A value of
4//! such a type holds a reference to the specified byte array or string. When such a reference is
5//! CBOR-encoded, rather than copying the entire byte array or string into the output, a small
6//! External Reference object is written containing the pointer to and length of the byte array or
7//! string. This CBOR can be passed to OC-Wasm which will read the data directly from its original
8//! memory location, eliminating the need to allocate enough memory and copy the data into the CBOR
9//! output.
10
11use minicbor::data::Tag;
12use minicbor::encode::{Encode, Encoder, Write};
13
14/// The External Reference CBOR tag number.
15const EXTERNAL_REFERENCE: Tag = Tag::new(32769);
16
17/// A reference to a byte array.
18pub struct Bytes<'a>(&'a [u8]);
19
20impl<'a> Bytes<'a> {
21 /// Wraps a byte array in a byte-array external reference.
22 ///
23 /// # Safety
24 /// It is not actually unsafe to construct a `Bytes` object. However, if the caller then
25 /// CBOR-encodes the resulting object, they must ensure that the `Bytes` object remains in
26 /// existence until the CBOR data has been submitted as part of a method call. Failure to do
27 /// this would allow the referent to be modified or dropped, resulting in OC-Wasm reading from
28 /// that reused memory.
29 #[must_use = "This function is only useful for its return value"]
30 pub const unsafe fn new(data: &'a [u8]) -> Self {
31 Self(data)
32 }
33}
34
35impl<'a, C> Encode<C> for Bytes<'a> {
36 fn encode<W: Write>(
37 &self,
38 e: &mut Encoder<W>,
39 _: &mut C,
40 ) -> Result<(), minicbor::encode::Error<W::Error>> {
41 const BYTE_STRING_MAJOR: u8 = 2;
42 // We’re building for WASM which is always 32-bit.
43 #[allow(clippy::cast_possible_truncation)]
44 e.tag(EXTERNAL_REFERENCE)?
45 .array(3)?
46 .u8(BYTE_STRING_MAJOR)?
47 .u32(self.0.as_ptr() as u32)?
48 .u32(self.0.len() as u32)?;
49 Ok(())
50 }
51}
52
53/// A reference to a text string.
54pub struct String<'a>(&'a str);
55
56impl<'a> String<'a> {
57 /// Wraps a string in a string external reference.
58 ///
59 /// # Safety
60 /// It is not actually unsafe to construct a `String` object. However, if the caller then
61 /// CBOR-encodes the resulting object, they must ensure that the `String` object remains in
62 /// existence until the CBOR data has been submitted as part of a method call. Failure to do
63 /// this would allow the referent to be modified or dropped, resulting in OC-Wasm reading from
64 /// that reused memory.
65 #[must_use = "This function is only useful for its return value"]
66 pub const unsafe fn new(data: &'a str) -> Self {
67 Self(data)
68 }
69}
70
71impl<'a, C> Encode<C> for String<'a> {
72 fn encode<W: Write>(
73 &self,
74 e: &mut Encoder<W>,
75 _: &mut C,
76 ) -> Result<(), minicbor::encode::Error<W::Error>> {
77 const UNICODE_STRING_MAJOR: u8 = 3;
78 // We’re building for WASM which is always 32-bit.
79 #[allow(clippy::cast_possible_truncation)]
80 e.tag(EXTERNAL_REFERENCE)?
81 .array(3)?
82 .u8(UNICODE_STRING_MAJOR)?
83 .u32(self.0.as_ptr() as u32)?
84 .u32(self.0.len() as u32)?;
85 Ok(())
86 }
87}