1#[doc(hidden)]
3#[macro_export]
4macro_rules! encode_with_extensions {
5 ($mode:ident) => {
6 #[inline]
9 pub fn encode<W, T>(self, writer: W, value: &T) -> Result<(), Error>
10 where
11 W: Writer,
12 T: ?Sized + Encode<$mode>,
13 {
14 $crate::allocator::with(|alloc| {
15 let cx = $crate::context::Same::new(alloc);
16 self.encode_with(&cx, writer, value)
17 })
18 }
19
20 #[cfg(feature = "std")]
23 #[inline]
24 pub fn to_writer<W, T>(self, write: W, value: &T) -> Result<(), Error>
25 where
26 W: io::Write,
27 T: ?Sized + Encode<$mode>,
28 {
29 let writer = $crate::wrap::wrap(write);
30 self.encode(writer, value)
31 }
32
33 #[cfg(feature = "std")]
36 #[inline]
37 pub fn to_writer_with<C, W, T>(self, cx: &C, write: W, value: &T) -> Result<(), C::Error>
38 where
39 C: ?Sized + Context<Mode = $mode>,
40 W: io::Write,
41 T: ?Sized + Encode<$mode>,
42 {
43 let writer = $crate::wrap::wrap(write);
44 self.encode_with(cx, writer, value)
45 }
46
47 #[cfg(feature = "alloc")]
49 #[inline]
50 pub fn to_vec<T>(self, value: &T) -> Result<Vec<u8>, Error>
51 where
52 T: ?Sized + Encode<$mode>,
53 {
54 let mut vec = Vec::new();
55 self.encode(&mut vec, value)?;
56 Ok(vec)
57 }
58
59 #[cfg(feature = "alloc")]
64 #[inline]
65 pub fn to_vec_with<C, T>(self, cx: &C, value: &T) -> Result<Vec<u8>, C::Error>
66 where
67 C: ?Sized + Context<Mode = $mode>,
68 T: ?Sized + Encode<$mode>,
69 {
70 let mut vec = Vec::new();
71 self.encode_with(cx, &mut vec, value)?;
72 Ok(vec)
73 }
74
75 #[inline]
78 pub fn to_fixed_bytes<const N: usize, T>(self, value: &T) -> Result<FixedBytes<N>, Error>
79 where
80 T: ?Sized + Encode<$mode>,
81 {
82 $crate::allocator::with(|alloc| {
83 let cx = $crate::context::Same::new(alloc);
84 self.to_fixed_bytes_with(&cx, value)
85 })
86 }
87
88 #[inline]
91 pub fn to_fixed_bytes_with<C, const N: usize, T>(
92 self,
93 cx: &C,
94 value: &T,
95 ) -> Result<FixedBytes<N>, C::Error>
96 where
97 C: ?Sized + Context<Mode = $mode>,
98 T: ?Sized + Encode<$mode>,
99 {
100 let mut bytes = FixedBytes::new();
101 self.encode_with(cx, &mut bytes, value)?;
102 Ok(bytes)
103 }
104 };
105}
106
107#[doc(hidden)]
109#[macro_export]
110macro_rules! encoding_from_slice_impls {
111 ($mode:ident) => {
112 #[inline]
115 pub fn from_slice<'de, T>(self, bytes: &'de [u8]) -> Result<T, Error>
116 where
117 T: Decode<'de, $mode>,
118 {
119 $crate::allocator::with(|alloc| {
120 let cx = $crate::context::Same::new(alloc);
121 self.from_slice_with(&cx, bytes)
122 })
123 }
124
125 #[inline]
131 pub fn from_slice_with<'de, C, T>(self, cx: &C, bytes: &'de [u8]) -> Result<T, C::Error>
132 where
133 C: ?Sized + Context<Mode = $mode>,
134 T: Decode<'de, $mode>,
135 {
136 let reader = $crate::reader::SliceReader::new(bytes);
137 self.decode_with(cx, reader)
138 }
139 };
140}
141
142#[doc(hidden)]
144#[macro_export]
145macro_rules! encoding_impls {
146 ($mode:ident, $encoder_new:path, $decoder_new:path) => {
147 #[inline]
153 pub fn encode_with<C, W, T>(self, cx: &C, writer: W, value: &T) -> Result<(), C::Error>
154 where
155 C: ?Sized + Context<Mode = $mode>,
156 W: Writer,
157 T: ?Sized + Encode<$mode>,
158 {
159 cx.clear();
160 T::encode(value, cx, $encoder_new(cx, writer))
161 }
162
163 #[inline]
169 pub fn decode_with<'de, C, R, T>(self, cx: &C, reader: R) -> Result<T, C::Error>
170 where
171 C: ?Sized + Context<Mode = $mode>,
172 R: Reader<'de>,
173 T: Decode<'de, $mode>,
174 {
175 cx.clear();
176 T::decode(cx, $decoder_new(cx, reader))
177 }
178
179 #[inline]
182 pub fn decode<'de, R, T>(self, reader: R) -> Result<T, Error>
183 where
184 R: Reader<'de>,
185 T: Decode<'de, $mode>,
186 {
187 $crate::allocator::with(|alloc| {
188 let cx = $crate::context::Same::new(alloc);
189 self.decode_with(&cx, reader)
190 })
191 }
192
193 $crate::encode_with_extensions!($mode);
194 };
195}
196
197#[doc(hidden)]
198#[macro_export]
199macro_rules! test_include_if {
200 (#[musli_value] => $($rest:tt)*) => { $($rest)* };
201 (=> $($_:tt)*) => {};
202}
203
204#[doc(hidden)]
206#[macro_export]
207#[allow(clippy::crate_in_macro_def)]
208macro_rules! test_fns {
209 ($what:expr, $mode:ty $(, $(#[$option:ident])*)?) => {
210 #[doc(hidden)]
212 #[track_caller]
213 #[cfg(feature = "test")]
214 pub fn rt<T>(value: T) -> T
215 where
216 T: ::musli::en::Encode<$mode> + ::musli::de::DecodeOwned<$mode>,
217 T: ::core::fmt::Debug + ::core::cmp::PartialEq,
218 {
219 const WHAT: &str = $what;
220 const ENCODING: crate::Encoding = crate::Encoding::new();
221
222 use ::core::any::type_name;
223 use ::alloc::string::ToString;
224
225 struct FormatBytes<'a>(&'a [u8]);
226
227 impl ::core::fmt::Display for FormatBytes<'_> {
228 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
229 write!(f, "b\"")?;
230
231 for b in self.0 {
232 if b.is_ascii_graphic() {
233 write!(f, "{}", *b as char)?;
234 } else {
235 write!(f, "\\x{b:02x}")?;
236 }
237 }
238
239 write!(f, "\" (0-{})", self.0.len())?;
240 Ok(())
241 }
242 }
243
244 $crate::allocator::with(|alloc| {
245 let mut cx = $crate::context::SystemContext::new(alloc);
246 cx.include_type();
247
248 let out = match ENCODING.to_vec_with(&cx, &value) {
249 Ok(out) => out,
250 Err(..) => {
251 let error = cx.report();
252 panic!("{WHAT}: {}: failed to encode:\n{error}", type_name::<T>())
253 }
254 };
255
256 let decoded: T = match ENCODING.from_slice_with(&cx, out.as_slice()) {
257 Ok(decoded) => decoded,
258 Err(..) => {
259 let out = FormatBytes(&out);
260 let error = cx.report();
261 panic!("{WHAT}: {}: failed to decode:\nValue: {value:?}\nBytes: {out}\n{error}", type_name::<T>())
262 }
263 };
264
265 assert_eq!(decoded, value, "{WHAT}: {}: roundtrip does not match\nValue: {value:?}", type_name::<T>());
266
267 $crate::test_include_if! {
268 $($(#[$option])*)* =>
269 let value_decode: ::musli_value::Value = match ENCODING.from_slice_with(&cx, out.as_slice()) {
270 Ok(decoded) => decoded,
271 Err(..) => {
272 let out = FormatBytes(&out);
273 let error = cx.report();
274 panic!("{WHAT}: {}: failed to decode to value type:\nValue: {value:?}\nBytes:{out}\n{error}", type_name::<T>())
275 }
276 };
277
278 let value_decoded: T = match ::musli_value::decode_with(&cx, &value_decode) {
279 Ok(decoded) => decoded,
280 Err(..) => {
281 let out = FormatBytes(&out);
282 let error = cx.report();
283 panic!("{WHAT}: {}: failed to decode from value type:\nValue: {value:?}\nBytes: {out}\nBuffered value: {value_decode:?}\n{error}", type_name::<T>())
284 }
285 };
286
287 assert_eq!(value_decoded, value, "{WHAT}: {}: musli-value roundtrip does not match\nValue: {value:?}", type_name::<T>());
288 }
289
290 decoded
291 })
292 }
293
294 #[doc(hidden)]
296 #[track_caller]
297 #[cfg(feature = "test")]
298 pub fn decode<'de, T, U>(value: T, out: &'de mut ::alloc::vec::Vec<u8>, expected: &U) -> U
299 where
300 T: ::musli::en::Encode<$mode>,
301 T: ::core::fmt::Debug + ::core::cmp::PartialEq,
302 U: ::musli::de::Decode<'de, $mode>,
303 U: ::core::fmt::Debug + ::core::cmp::PartialEq,
304 {
305 const WHAT: &str = $what;
306 const ENCODING: crate::Encoding = crate::Encoding::new();
307
308 use ::core::any::type_name;
309 use ::alloc::string::ToString;
310
311 struct FormatBytes<'a>(&'a [u8]);
312
313 impl ::core::fmt::Display for FormatBytes<'_> {
314 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
315 write!(f, "b\"")?;
316
317 for b in self.0 {
318 if b.is_ascii_graphic() {
319 write!(f, "{}", *b as char)?;
320 } else {
321 write!(f, "\\x{b:02x}")?;
322 }
323 }
324
325 write!(f, "\" (0-{})", self.0.len())?;
326 Ok(())
327 }
328 }
329
330 $crate::allocator::with(|alloc| {
331 let mut cx = $crate::context::SystemContext::new(alloc);
332 cx.include_type();
333
334 out.clear();
335
336 match ENCODING.to_writer_with(&cx, &mut *out, &value) {
337 Ok(()) => (),
338 Err(..) => {
339 let error = cx.report();
340 panic!("{WHAT}: {}: failed to encode:\n{error}", type_name::<T>())
341 }
342 };
343
344 let actual = match ENCODING.from_slice_with(&cx, &*out) {
345 Ok(decoded) => decoded,
346 Err(error) => {
347 let out = FormatBytes(&*out);
348 let error = cx.report();
349 panic!("{WHAT}: {}: failed to decode:\nValue: {value:?}\nBytes: {out}\n{error}", type_name::<T>())
350 }
351 };
352
353 assert_eq!(
354 actual,
355 *expected,
356 "{WHAT}: decoded value does not match expected\nBytes: {}",
357 FormatBytes(&*out),
358 );
359
360 actual
361 })
362 }
363
364 #[doc(hidden)]
366 #[track_caller]
367 #[cfg(feature = "test")]
368 pub fn to_vec<T>(value: T) -> ::alloc::vec::Vec<u8>
369 where
370 T: ::musli::en::Encode<$mode>,
371 {
372 const WHAT: &str = $what;
373 const ENCODING: crate::Encoding = crate::Encoding::new();
374
375 use ::core::any::type_name;
376 use ::alloc::string::ToString;
377
378 $crate::allocator::with(|alloc| {
379 let mut cx = $crate::context::SystemContext::new(alloc);
380 cx.include_type();
381
382 match ENCODING.to_vec_with(&cx, &value) {
383 Ok(out) => out,
384 Err(..) => {
385 let error = cx.report();
386 panic!("{WHAT}: {}: failed to encode:\n{error}", type_name::<T>())
387 }
388 }
389 })
390 }
391 }
392}
393
394#[doc(hidden)]
397#[macro_export]
398macro_rules! simdutf8 {
399 () => {
400 pub(crate) mod str {
401 #[cfg(feature = "alloc")]
405 use alloc::string::String;
406 #[cfg(feature = "alloc")]
407 use alloc::vec::Vec;
408
409 use core::fmt;
410
411 #[cfg(not(feature = "simdutf8"))]
412 #[doc(inline)]
413 pub use core::str::from_utf8;
414 #[cfg(feature = "simdutf8")]
415 #[doc(inline)]
416 pub use simdutf8::basic::from_utf8;
417
418 #[non_exhaustive]
420 #[derive(Debug)]
421 pub struct Utf8Error;
422
423 #[cfg(feature = "std")]
424 impl std::error::Error for Utf8Error {}
425
426 impl fmt::Display for Utf8Error {
427 #[inline]
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 write!(f, "invalid or incomplete utf-8 sequence")
430 }
431 }
432
433 #[inline(always)]
438 #[cfg(all(feature = "alloc", not(feature = "simdutf8")))]
439 pub fn from_utf8_owned(bytes: Vec<u8>) -> Result<String, Utf8Error> {
440 match String::from_utf8(bytes) {
441 Ok(string) => Ok(string),
442 Err(..) => Err(Utf8Error),
443 }
444 }
445
446 #[inline(always)]
451 #[cfg(all(feature = "alloc", feature = "simdutf8"))]
452 pub fn from_utf8_owned(bytes: Vec<u8>) -> Result<String, Utf8Error> {
453 if from_utf8(&bytes).is_err() {
454 return Err(Utf8Error);
455 }
456
457 Ok(unsafe { String::from_utf8_unchecked(bytes) })
459 }
460 }
461 };
462}