jam_program_blob/
program_blob.rs

1use alloc::{borrow::Cow, string::String, vec::Vec};
2use scale::{Compact, Decode, Encode};
3
4/// Information on a crate, useful for building conventional metadata of type 0.
5#[derive(Clone, PartialEq, Eq, Encode, Decode)]
6pub struct CrateInfo {
7	pub name: String,
8	pub version: String,
9	pub license: String,
10	pub authors: Vec<String>,
11}
12
13/// Information which, when encoded, could fill a program blob's metadata.
14#[derive(Clone, PartialEq, Eq, Encode, Decode)]
15pub enum ConventionalMetadata {
16	Info(CrateInfo),
17}
18
19/// A JAM-specific program blob.
20pub struct ProgramBlob<'a> {
21	pub metadata: Cow<'a, [u8]>,
22	pub ro_data: Cow<'a, [u8]>,
23	pub rw_data: Cow<'a, [u8]>,
24	pub code_blob: Cow<'a, [u8]>,
25	pub rw_data_padding_pages: u16,
26	pub stack_size: u32,
27}
28
29fn read_u24(bytes: &mut &[u8]) -> Option<u32> {
30	let xs = bytes.get(..3)?;
31	*bytes = &bytes[3..];
32	Some(u32::from_le_bytes([xs[0], xs[1], xs[2], 0]))
33}
34
35fn write_u24(value: u32, output: &mut Vec<u8>) -> Result<(), ()> {
36	if value >= (1 << 24) {
37		return Err(());
38	}
39
40	output.extend_from_slice(&value.to_le_bytes()[0..3]);
41	Ok(())
42}
43
44#[test]
45fn read_write_u24() {
46	let mut output = Vec::new();
47	write_u24(0x00345678, &mut output).unwrap();
48	assert_eq!(read_u24(&mut &output[..]), Some(0x00345678));
49
50	assert!(write_u24(0x00ffffff, &mut output).is_ok());
51	assert!(write_u24(0x01000000, &mut output).is_err());
52}
53
54fn read_u16(bytes: &mut &[u8]) -> Option<u16> {
55	let xs = bytes.get(..2)?;
56	*bytes = &bytes[2..];
57	Some(u16::from_le_bytes([xs[0], xs[1]]))
58}
59
60fn read_u32(bytes: &mut &[u8]) -> Option<u32> {
61	let xs = bytes.get(..4)?;
62	*bytes = &bytes[4..];
63	Some(u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]))
64}
65
66fn read_var(bytes: &mut &[u8]) -> Option<u32> {
67	Some(Compact::<u32>::decode(bytes).ok()?.0)
68}
69
70fn write_var(value: u32, output: &mut Vec<u8>) {
71	Compact::<u32>(value).encode_to(output)
72}
73
74#[test]
75fn read_write_var() {
76	let mut output = Vec::new();
77	let vals = [0x00345678, 0x00, 0x01, 0x7f, 0x80, 0xffffffff];
78	for i in vals.into_iter() {
79		write_var(i, &mut output);
80	}
81	let mut cursor = output.as_ref();
82	for i in vals.into_iter() {
83		assert_eq!(read_var(&mut cursor), Some(i));
84	}
85}
86
87fn read_cow<'a>(bytes: &mut &'a [u8], length: u32) -> Option<Cow<'a, [u8]>> {
88	let length = length as usize;
89	let cow = bytes.get(..length)?;
90	*bytes = &bytes[length..];
91	Some(Cow::Borrowed(cow))
92}
93
94impl<'a> ProgramBlob<'a> {
95	pub fn from_bytes(mut bytes: &'a [u8]) -> Option<Self> {
96		let offset = read_var(&mut bytes)?;
97		let metadata = read_cow(&mut bytes, offset)?;
98		let ro_data_len = read_u24(&mut bytes)?;
99		let rw_data_len = read_u24(&mut bytes)?;
100		let rw_data_padding_pages = read_u16(&mut bytes)?;
101		let stack_size = read_u24(&mut bytes)?;
102		let ro_data = read_cow(&mut bytes, ro_data_len)?;
103		let rw_data = read_cow(&mut bytes, rw_data_len)?;
104		let code_blob_len = read_u32(&mut bytes)?;
105		let code_blob = read_cow(&mut bytes, code_blob_len)?;
106
107		if !bytes.is_empty() {
108			return None;
109		}
110
111		Some(ProgramBlob {
112			metadata,
113			rw_data_padding_pages,
114			stack_size,
115			ro_data,
116			rw_data,
117			code_blob,
118		})
119	}
120
121	pub fn to_vec(&self) -> Result<Vec<u8>, &'static str> {
122		let mut output = Vec::new();
123		write_var(
124			u32::try_from(self.metadata.len()).map_err(|_| "metadata too large")?,
125			&mut output,
126		);
127		output.extend_from_slice(&self.metadata);
128		write_u24(u32::try_from(self.ro_data.len()).map_err(|_| "too large RO data")?, &mut output)
129			.map_err(|_| "too large RO data")?;
130		write_u24(u32::try_from(self.rw_data.len()).map_err(|_| "too large RW data")?, &mut output)
131			.map_err(|_| "too large RW data")?;
132		output.extend_from_slice(&self.rw_data_padding_pages.to_le_bytes());
133		write_u24(self.stack_size, &mut output).map_err(|_| "too large stack size")?;
134		output.extend_from_slice(&self.ro_data);
135		output.extend_from_slice(&self.rw_data);
136		output.extend_from_slice(
137			&u32::try_from(self.code_blob.len()).map_err(|_| "too large code")?.to_le_bytes(),
138		);
139		output.extend_from_slice(&self.code_blob);
140		Ok(output)
141	}
142}