esexpr_binary/
writer.rs

1use alloc::borrow::ToOwned;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::convert::Infallible;
5use core::marker::PhantomData;
6
7use derive_more::From;
8
9/// Error type for `ExprGenerator`
10#[derive(From, Debug)]
11pub enum GeneratorError<E> {
12	/// An IO error occurred
13	IOError(E),
14}
15
16impl<E: 'static> GeneratorError<E> {
17	/// Convert from an `Infallible` error
18	pub fn from_infalliable(error: GeneratorError<Infallible>) -> Self {
19		match error {
20			GeneratorError::IOError(e) => match e {},
21		}
22	}
23}
24
25/// Generator for `ESExpr`'s binary format
26pub struct ExprGenerator<'a, W, E> {
27	out: &'a mut W,
28	string_pool: Vec<String>,
29	error: PhantomData<E>,
30}
31
32impl<'a, W, E> ExprGenerator<'a, W, E> {
33	/// Create an `ExprGenerator`
34	pub fn new(out: &'a mut W) -> Self {
35		ExprGenerator {
36			out,
37			string_pool: Vec::new(),
38			error: PhantomData,
39		}
40	}
41
42	/// Create an `ExprGenerator` with an existing string pool
43	pub fn new_with_string_pool(out: &'a mut W, string_pool: Vec<String>) -> Self {
44		ExprGenerator {
45			out,
46			string_pool,
47			error: PhantomData,
48		}
49	}
50}
51
52macro_rules! writer_mod {
53	($syncness: ident) => {
54		use alloc::borrow::Borrow;
55		use core::convert::Infallible;
56
57		use esexpr::{ESExpr, ESExprConstructor};
58		use half::f16;
59		use num_bigint::{BigUint, Sign};
60
61		use super::*;
62		use crate::async_macros::{do_await, maybe_async};
63		use crate::format::*;
64
65		/// Defines `ESExpr` generation for binary file format
66		#[allow(async_fn_in_trait, reason = "No additional traits to add")]
67		pub trait ExprGeneratorWrite<E> {
68			maybe_async!(
69				$syncness,
70				/// Generate output for an expression
71				///
72				/// # Errors
73				/// Returns `Err` if an error occurs during generation.
74				fn generate(&mut self, expr: &ESExpr<'_>) -> Result<(), GeneratorError<E>>;
75			);
76		}
77
78		pub(super) trait ExprGeneratorWriteExt<W, E>: ExprGeneratorWrite<E> {
79			maybe_async!(
80				$syncness,
81				fn generate_expr(&mut self, expr: &ESExpr) -> Result<(), GeneratorError<E>>;
82			);
83			maybe_async!(
84				$syncness,
85				fn get_string_pool_index<S: Borrow<str>>(&mut self, s: S) -> Result<usize, GeneratorError<E>>;
86			);
87			maybe_async!(
88				$syncness,
89				fn write_int_tag(&mut self, tag: u8, i: &BigUint) -> Result<(), GeneratorError<E>>;
90			);
91			maybe_async!(
92				$syncness,
93				fn write_int_tag_out(out: &mut W, tag: u8, i: &BigUint) -> Result<(), GeneratorError<E>>;
94			);
95			maybe_async!(
96				$syncness,
97				fn write_int_full(out: &mut W, i: &BigUint) -> Result<(), GeneratorError<E>>;
98			);
99			maybe_async!(
100				$syncness,
101				fn write_int_rest(
102					out: &mut W,
103					buff: &[u8],
104					current: u8,
105					bit_index: i32,
106				) -> Result<(), GeneratorError<E>>;
107			);
108			maybe_async!(
109				$syncness,
110				fn write_string_expr(out: &mut W, s: &str) -> Result<(), GeneratorError<E>>;
111			);
112			maybe_async!(
113				$syncness,
114				fn write(&mut self, b: u8) -> Result<(), GeneratorError<E>>;
115			);
116			maybe_async!(
117				$syncness,
118				fn write_out(out: &mut W, b: u8) -> Result<(), GeneratorError<E>>;
119			);
120		}
121
122		impl<'a, E: 'static, W: Write<E>> ExprGeneratorWrite<E> for ExprGenerator<'a, W, E> {
123			maybe_async!(
124				$syncness,
125				fn generate(&mut self, expr: &ESExpr<'_>) -> Result<(), GeneratorError<E>> {
126					let old_string_pool_end = self.string_pool.len();
127
128					let mut generator: ExprGenerator<_, Infallible> = ExprGenerator {
129						out: &mut crate::io::sink(),
130						string_pool: Vec::new(),
131						error: PhantomData,
132					};
133
134					core::mem::swap(&mut self.string_pool, &mut generator.string_pool);
135					// Dummy generator to catch new strings
136					<ExprGenerator<_, Infallible> as super::writer_sync::ExprGeneratorWriteExt<_, Infallible>>::generate_expr(
137																						&mut generator,
138																						expr,
139																					).map_err(GeneratorError::from_infalliable)?;
140
141					core::mem::swap(&mut self.string_pool, &mut generator.string_pool);
142
143					match &self.string_pool[old_string_pool_end..] {
144						[] => {},
145						[s] => {
146							do_await!($syncness, Self::write_out(self.out, TAG_APPEND_STRING_TABLE))?;
147							do_await!($syncness, Self::write_string_expr(self.out, s.as_str()))?;
148						},
149						new_strings => {
150							do_await!($syncness, Self::write_out(self.out, TAG_APPEND_STRING_TABLE))?;
151							do_await!(
152								$syncness,
153								Self::write_out(self.out, TAG_CONSTRUCTOR_START_STRING_TABLE)
154							)?;
155							for s in new_strings {
156								do_await!($syncness, Self::write_string_expr(self.out, s))?;
157							}
158							do_await!($syncness, Self::write_out(self.out, TAG_CONSTRUCTOR_END))?;
159						},
160					}
161
162					do_await!($syncness, self.generate_expr(expr))?;
163					Ok(())
164				}
165			);
166		}
167
168		impl<'a, E: 'static, W: Write<E>> ExprGeneratorWriteExt<W, E> for ExprGenerator<'a, W, E> {
169			maybe_async!(
170				$syncness,
171				fn generate_expr(&mut self, expr: &ESExpr<'_>) -> Result<(), GeneratorError<E>> {
172					match expr {
173						ESExpr::Constructor(ESExprConstructor { name, args, kwargs }) => {
174							match &**name {
175								"string-table" => do_await!($syncness, self.write(TAG_CONSTRUCTOR_START_STRING_TABLE))?,
176								"list" => do_await!($syncness, self.write(TAG_CONSTRUCTOR_START_LIST))?,
177								_ => {
178									let index = do_await!($syncness, self.get_string_pool_index(name))?;
179									do_await!(
180										$syncness,
181										self.write_int_tag(TAG_VARINT_CONSTRUCTOR_START, &BigUint::from(index))
182									)?;
183								},
184							}
185
186							for arg in args.iter() {
187								do_await!($syncness, self.generate_expr(&arg))?;
188							}
189
190							for (kw, value) in kwargs.iter() {
191								let index = do_await!($syncness, self.get_string_pool_index(kw))?;
192								do_await!(
193									$syncness,
194									self.write_int_tag(TAG_VARINT_KEYWORD, &BigUint::from(index))
195								)?;
196								do_await!($syncness, self.generate_expr(&value))?;
197							}
198
199							do_await!($syncness, self.write(TAG_CONSTRUCTOR_END))?;
200						},
201						ESExpr::Bool(true) => {
202							do_await!($syncness, self.write(TAG_TRUE))?;
203						},
204						ESExpr::Bool(false) => {
205							do_await!($syncness, self.write(TAG_FALSE))?;
206						},
207						ESExpr::Int(i) => {
208							let (sign, mut magnitude) = i.as_ref().clone().into_parts();
209
210							match sign {
211								Sign::NoSign | Sign::Plus => {
212									do_await!($syncness, self.write_int_tag(TAG_VARINT_NON_NEG_INT, &magnitude))?;
213								},
214
215								Sign::Minus => {
216									magnitude -= 1usize;
217									do_await!($syncness, self.write_int_tag(TAG_VARINT_NEG_INT, &magnitude))?;
218								},
219							}
220						},
221						ESExpr::Float16(f) => {
222							do_await!($syncness, self.write(TAG_FLOAT16))?;
223							do_await!($syncness, self.out.write(&f16::to_le_bytes(*f)))?;
224						},
225						ESExpr::Float32(f) => {
226							do_await!($syncness, self.write(TAG_FLOAT32))?;
227							do_await!($syncness, self.out.write(&f32::to_le_bytes(*f)))?;
228						},
229						ESExpr::Float64(d) => {
230							do_await!($syncness, self.write(TAG_FLOAT64))?;
231							do_await!($syncness, self.out.write(&f64::to_le_bytes(*d)))?;
232						},
233						ESExpr::Str(s) => {
234							do_await!(
235								$syncness,
236								self.write_int_tag(TAG_VARINT_STRING_LENGTH, &BigUint::from(s.len()))
237							)?;
238							do_await!($syncness, self.out.write(s.as_bytes()))?;
239						},
240
241						ESExpr::Array8(b) => {
242							do_await!(
243								$syncness,
244								self.write_int_tag(TAG_VARINT_ARRAY8_LENGTH, &BigUint::from(b.len()))
245							)?;
246							do_await!($syncness, self.out.write(b.as_ref()))?;
247						},
248						ESExpr::Array16(b) => {
249							do_await!($syncness, self.write(TAG_ARRAY16))?;
250							do_await!($syncness, Self::write_int_full(self.out, &BigUint::from(b.len())))?;
251							
252							#[cfg(target_endian = "little")]
253							{
254								do_await!(
255									$syncness,
256									self.out.write(bytemuck::cast_slice::<u16, u8>(b.as_ref()))
257								)?;
258							}
259							
260							#[cfg(target_endian = "big")]
261							{
262								for value in b.iter().copied() {
263									do_await!(
264										$syncness,
265										self.out.write(&value.to_le_bytes())
266									)?;
267								}
268							}
269						},
270						ESExpr::Array32(b) => {
271							do_await!($syncness, self.write(TAG_ARRAY32))?;
272							do_await!($syncness, Self::write_int_full(self.out, &BigUint::from(b.len())))?;
273							
274							#[cfg(target_endian = "little")]
275							{
276								do_await!(
277									$syncness,
278									self.out.write(bytemuck::cast_slice::<u32, u8>(b.as_ref()))
279								)?;
280							}
281							
282							#[cfg(target_endian = "big")]
283							{
284								for value in b.iter().copied() {
285									do_await!(
286										$syncness,
287										self.out.write(&value.to_le_bytes())
288									)?;
289								}
290							}
291						},
292						ESExpr::Array64(b) => {
293							do_await!($syncness, self.write(TAG_ARRAY64))?;
294							do_await!($syncness, Self::write_int_full(self.out, &BigUint::from(b.len())))?;
295							
296							#[cfg(target_endian = "little")]
297							{
298								do_await!(
299									$syncness,
300									self.out.write(bytemuck::cast_slice::<u64, u8>(b.as_ref()))
301								)?;
302							}
303							
304							#[cfg(target_endian = "big")]
305							{
306								for value in b.iter().copied() {
307									do_await!(
308										$syncness,
309										self.out.write(&value.to_le_bytes())
310									)?;
311								}
312							}
313						},
314						ESExpr::Array128(b) => {
315							do_await!($syncness, self.write(TAG_ARRAY128))?;
316							do_await!($syncness, Self::write_int_full(self.out, &BigUint::from(b.len())))?;
317							
318							#[cfg(target_endian = "little")]
319							{
320								do_await!(
321									$syncness,
322									self.out.write(bytemuck::cast_slice::<u128, u8>(b.as_ref()))
323								)?;
324							}
325							
326							#[cfg(target_endian = "big")]
327							{
328								for value in b.iter().copied() {
329									do_await!(
330										$syncness,
331										self.out.write(&value.to_le_bytes())
332									)?;
333								}
334							}
335						},
336
337						ESExpr::Null(level) => {
338							let level: &BigUint = level.as_ref();
339
340							if *level == BigUint::ZERO {
341								do_await!($syncness, self.write(TAG_NULL0))?;
342							}
343							else if *level == BigUint::from(1u32) {
344								do_await!($syncness, self.write(TAG_NULL1))?;
345							}
346							else if *level == BigUint::from(2u32) {
347								do_await!($syncness, self.write(TAG_NULL2))?;
348							}
349							else {
350								do_await!($syncness, self.write(TAG_NULLN))?;
351								do_await!($syncness, Self::write_int_full(self.out, &(level - 3u32)))?;
352							}
353						},
354					}
355
356					Ok(())
357				}
358			);
359
360			maybe_async!(
361				$syncness,
362				fn get_string_pool_index<S: Borrow<str>>(&mut self, s: S) -> Result<usize, GeneratorError<E>> {
363					let s = s.borrow();
364					if let Some(index) = self.string_pool.iter().position(|s2| s2 == s) {
365						return Ok(index);
366					}
367
368					let index = self.string_pool.len();
369					self.string_pool.push(s.to_owned());
370
371					do_await!($syncness, self.write(TAG_APPEND_STRING_TABLE))?;
372					do_await!($syncness, Self::write_string_expr(self.out, s))?;
373
374					Ok(index)
375				}
376			);
377
378			maybe_async!(
379				$syncness,
380				fn write_int_tag(&mut self, tag: u8, i: &BigUint) -> Result<(), GeneratorError<E>> {
381					do_await!($syncness, Self::write_int_tag_out(self.out, tag, i))
382				}
383			);
384
385			maybe_async!(
386				$syncness,
387				fn write_int_tag_out(out: &mut W, tag: u8, i: &BigUint) -> Result<(), GeneratorError<E>> {
388					let buff = i.to_bytes_le();
389
390					let b0 = buff.first().copied().unwrap_or_default();
391					let mut current = tag | (b0 & 0x0F);
392					if buff.len() < 2 && (b0 & 0xF0) == 0 {
393						do_await!($syncness, Self::write_out(out, current))?;
394						return Ok(());
395					}
396
397					current |= 0x10;
398					do_await!($syncness, Self::write_out(out, current))?;
399
400					current = b0 >> 4;
401					let bit_index = 4;
402
403					do_await!(
404						$syncness,
405						Self::write_int_rest(out, &buff[1..], current, bit_index)
406					)
407				}
408			);
409
410			maybe_async!(
411				$syncness,
412				fn write_int_full(out: &mut W, i: &BigUint) -> Result<(), GeneratorError<E>> {
413					if *i == BigUint::ZERO {
414						do_await!($syncness, Self::write_out(out, 0))?;
415						return Ok(());
416					}
417
418					let buff = i.to_bytes_le();
419					let current = 0;
420					let bit_index = 0;
421
422					do_await!($syncness, Self::write_int_rest(out, &buff, current, bit_index))
423				}
424			);
425
426			maybe_async!(
427				$syncness,
428				fn write_int_rest(
429					out: &mut W,
430					buff: &[u8],
431					mut current: u8,
432					mut bit_index: i32,
433				) -> Result<(), GeneratorError<E>> {
434					for (i, b) in buff.iter().copied().enumerate() {
435						let mut bit_index2 = 0;
436						while bit_index2 < 8 {
437							let written_bits = core::cmp::min(7 - bit_index, 8 - bit_index2);
438							current |= ((b >> bit_index2) & 0x7F) << bit_index;
439
440							bit_index += written_bits;
441							bit_index2 += written_bits;
442							if bit_index >= 7 {
443								if i < buff.len() - 1 || (bit_index2 < 8 && (b >> bit_index2) != 0) {
444									current |= 0x80;
445								}
446
447								do_await!($syncness, Self::write_out(out, current))?;
448								bit_index = 0;
449								current = 0;
450							}
451						}
452					}
453
454					if current != 0 {
455						do_await!($syncness, Self::write_out(out, current))?;
456					}
457
458					Ok(())
459				}
460			);
461
462			maybe_async!(
463				$syncness,
464				fn write_string_expr(out: &mut W, s: &str) -> Result<(), GeneratorError<E>> {
465					do_await!(
466						$syncness,
467						Self::write_int_tag_out(out, TAG_VARINT_STRING_LENGTH, &BigUint::from(s.len()))
468					)?;
469					do_await!($syncness, out.write(s.as_bytes()))?;
470					Ok(())
471				}
472			);
473
474			maybe_async!(
475				$syncness,
476				fn write(&mut self, b: u8) -> Result<(), GeneratorError<E>> {
477					do_await!($syncness, Self::write_out(self.out, b))
478				}
479			);
480
481			maybe_async!(
482				$syncness,
483				fn write_out(out: &mut W, b: u8) -> Result<(), GeneratorError<E>> {
484					Ok(do_await!($syncness, out.write(core::slice::from_ref(&b)))?)
485				}
486			);
487		}
488	};
489}
490
491mod writer_sync {
492	use crate::io::Write;
493	writer_mod!(sync);
494}
495
496mod writer_async {
497	use crate::io::AsyncWrite as Write;
498	writer_mod!(async);
499}
500
501pub use writer_async::ExprGeneratorWrite as ExprGeneratorAsync;
502pub use writer_sync::ExprGeneratorWrite as ExprGeneratorSync;