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}