diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef2a124b2..ea8f748e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - run: cargo build -p musli --all-features + - run: cargo build -p musli --features test if: matrix.rust != 'stable' - run: cargo build --all-targets --features test if: matrix.rust == 'stable' @@ -140,7 +140,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: Swatinem/rust-cache@v2 - - run: cargo doc --lib --no-deps --document-private-items + - run: cargo doc --features test --lib --no-deps --document-private-items env: RUSTFLAGS: --cfg doc_cfg - RUSTDOCFLAGS: --cfg doc_cfg -Dwarnings + RUSTDOCFLAGS: --cfg doc_cfg -D warnings diff --git a/README.md b/README.md index 2439c4119..0e1ac2e81 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ low-level refreshingly simple [zero-copy serialization][zerocopy]. ## Overview * See [`derives`] to learn how to implement [`Encode`] and [`Decode`]. +* See [`data_model`] to learn about the abstract data model of Müsli. * See [benchmarks] and [size comparisons] to learn about the performance of this framework. * See [`tests`] to learn how this library is tested. @@ -117,47 +118,38 @@ fn without_musli(storage: &Storage) -> Result<[u8; 8]> { ## Müsli is different from [`serde`] -* We make use of GATs to provide tighter abstractions. GATs were not - available when serde was designed. -* When decoding or encoding we operate by the principle that most things - return either return a [`Decoder`] or [`Encoder`]. This means for example - that field names are not restricted to be strings or indexes, but can be - renamed to [completely arbitrary types][musli-name-type]. -* We make less use of the Visitor pattern in certain instances where it's - deemed unnecessary, such as [when decoding collections]. The result is - usually cleaner decode implementations like below. -* We make use of [*moded encoding*](#Modes) allowing the same struct to be - encoded in many different ways. -* We support [detailed tracing] when decoding for rich diagnostics. -* Müsli was designed to support [no-std and no-alloc] environments from the - ground up without compromising on features using a safe and efficient - [scoped allocations]. +**Müsli's data model does not speak Rust**. There are no +`serialize_struct_variant` methods which provides metadata about the type +being serialized. The [`Encoder`] and [`Decoder`] traits are agnostic on +this. Compatibility with Rust types is entirely handled using the [`Encode`] +and [`Decode`] derives in combination with [modes](#Modes). -```rust -use musli::Context; -use musli::de::{Decode, Decoder, SequenceDecoder}; +**We use GATs** to provide easier to use abstractions. GATs were not +available when serde was designed. -struct MyType { - data: Vec, -} +**Everything is a [`Decoder`] or [`Encoder`]**. Field names are therefore +not limited to be strings or indexes, but can be named to [arbitrary +types][musli-name-type] if needed. -impl<'de, M> Decode<'de, M> for MyType { - fn decode(cx: &D::Cx, decoder: D) -> Result - where - D: Decoder<'de, Mode = M>, - { - decoder.decode_sequence(|seq| { - let mut data = Vec::with_capacity(seq.size_hint().or_default()); - - while let Some(decoder) = seq.try_decode_next()? { - data.push(decoder.decode()?); - } - - Ok(Self { data }) - }) - } -} -``` +**Visitor are only used when needed**. `serde` [completely uses visitors] +when deserializing and the corresponding method is treated as a "hint" to +the underlying format. The deserializer is then free to call any method on +the visitor depending on what the underlying format actually contains. In +Müsli, we swap this around. If the caller wants to decode an arbitrary type +it calls [`decode_any`]. The format can then either signal the appropriate +underlying type or call [`Visitor::visit_unknown`] telling the implementer +that it does not have access to type information. + +**We've invented [*moded encoding*](#Modes)** allowing the same Rust types +to be encoded in many different ways with much greater control over how +things encoded. By default we include the [`Binary`] and [`Text`] modes +providing sensible defaults for binary and text-based formats. + +**Müsli fully supports [no-std and no-alloc]** from the ground up without +compromising on features using safe and efficient [scoped allocations]. + +**We support [detailed tracing]** when decoding for much improved +diagnostics of *where* something went wrong.
@@ -360,6 +352,8 @@ safety, extensive testing and fuzzing is performed using `miri`. See [`Binary`]: [`bincode`]: +[`data_model`]: +[`decode_any`]: https://docs.rs/musli/latest/musli/trait.Decoder.html#method.decode_any [`Decode`]: [`Decoder`]: [`derives`]: @@ -375,12 +369,14 @@ safety, extensive testing and fuzzing is performed using `miri`. See [`serde`]: [`simdutf8`]: [`tests`]: +[`Text`]: +[`Visitor::visit_unknown`]: https://docs.rs/musli/latest/musli/de/trait.Visitor.html#method.visit_unknown [benchmarks]: [bit packing]: +[completely uses visitors]: https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_u32 [detailed tracing]: [musli-name-type]: [no-std and no-alloc]: [scoped allocations]: [size comparisons]: -[when decoding collections]: [zerocopy]: diff --git a/crates/musli-core/src/de/decoder.rs b/crates/musli-core/src/de/decoder.rs index 89fee3dd9..0e0ad6b12 100644 --- a/crates/musli-core/src/de/decoder.rs +++ b/crates/musli-core/src/de/decoder.rs @@ -8,7 +8,7 @@ use crate::Context; use super::{ AsDecoder, Decode, DecodeUnsized, DecodeUnsizedBytes, EntriesDecoder, MapDecoder, - NumberVisitor, SequenceDecoder, Skip, ValueVisitor, VariantDecoder, Visitor, + SequenceDecoder, Skip, UnsizedVisitor, VariantDecoder, Visitor, }; /// Trait governing the implementation of a decoder. @@ -229,15 +229,15 @@ pub trait Decoder<'de>: Sized { /// where /// D: Decoder<'de>, /// { - /// decoder.decode_unit()?; + /// decoder.decode_empty()?; /// Ok(UnitType) /// } /// } /// ``` #[inline] - fn decode_unit(self) -> Result<(), ::Error> { + fn decode_empty(self) -> Result<(), ::Error> { Err(self.cx().message(expecting::unsupported_type( - &expecting::Unit, + &expecting::Empty, ExpectingWrapper::new(&self), ))) } @@ -898,19 +898,6 @@ pub trait Decoder<'de>: Sized { ))) } - /// Decode an unknown number using a visitor that can handle arbitrary - /// precision numbers. - #[inline] - fn decode_number(self, visitor: V) -> Result::Error> - where - V: NumberVisitor<'de, Self::Cx>, - { - Err(self.cx().message(expecting::unsupported_type( - &expecting::Number, - ExpectingWrapper::new(&self), - ))) - } - /// Decode a fixed-length array. /// /// # Examples @@ -974,7 +961,7 @@ pub trait Decoder<'de>: Sized { /// use std::fmt; /// /// use musli::{Context, Decode, Decoder}; - /// use musli::de::ValueVisitor; + /// use musli::de::UnsizedVisitor; /// # struct BytesReference<'de> { data: &'de [u8] } /// /// impl<'de, M> Decode<'de, M> for BytesReference<'de> { @@ -985,7 +972,7 @@ pub trait Decoder<'de>: Sized { /// { /// struct Visitor; /// - /// impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor + /// impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor /// where /// C: ?Sized + Context, /// { @@ -1011,7 +998,7 @@ pub trait Decoder<'de>: Sized { #[inline] fn decode_bytes(self, visitor: V) -> Result::Error> where - V: ValueVisitor<'de, Self::Cx, [u8]>, + V: UnsizedVisitor<'de, Self::Cx, [u8]>, { Err(self.cx().message(expecting::unsupported_type( &expecting::Bytes, @@ -1041,7 +1028,7 @@ pub trait Decoder<'de>: Sized { /// use std::fmt; /// /// use musli::{Context, Decode, Decoder}; - /// use musli::de::ValueVisitor; + /// use musli::de::UnsizedVisitor; /// # struct StringReference<'de> { data: &'de str } /// /// impl<'de, M> Decode<'de, M> for StringReference<'de> { @@ -1052,7 +1039,7 @@ pub trait Decoder<'de>: Sized { /// { /// struct Visitor; /// - /// impl<'de, C> ValueVisitor<'de, C, str> for Visitor + /// impl<'de, C> UnsizedVisitor<'de, C, str> for Visitor /// where /// C: ?Sized + Context, /// { @@ -1078,7 +1065,7 @@ pub trait Decoder<'de>: Sized { #[inline] fn decode_string(self, visitor: V) -> Result::Error> where - V: ValueVisitor<'de, Self::Cx, str>, + V: UnsizedVisitor<'de, Self::Cx, str>, { Err(self.cx().message(expecting::unsupported_type( &expecting::String, @@ -1263,7 +1250,7 @@ pub trait Decoder<'de>: Sized { F: FnOnce(&mut Self::DecodeSequence) -> Result::Error>, { Err(self.cx().message(expecting::unsupported_type( - &expecting::Sequence, + &expecting::UnsizedSequence, ExpectingWrapper::new(&self), ))) } @@ -1301,7 +1288,7 @@ pub trait Decoder<'de>: Sized { F: FnOnce(&mut Self::DecodeSequenceHint) -> Result::Error>, { Err(self.cx().message(expecting::unsupported_type( - &expecting::Sequence, + &expecting::SequenceWith(hint.size_hint()), ExpectingWrapper::new(&self), ))) } @@ -1339,7 +1326,7 @@ pub trait Decoder<'de>: Sized { F: FnOnce(&mut Self::DecodeMap) -> Result::Error>, { Err(self.cx().message(expecting::unsupported_type( - &expecting::UnsizedStruct, + &expecting::UnsizedMap, ExpectingWrapper::new(&self), ))) } @@ -1481,6 +1468,18 @@ pub trait Decoder<'de>: Sized { ))) } + /// Decode an unknown number using a visitor. + #[inline] + fn decode_number(self, visitor: V) -> Result::Error> + where + V: Visitor<'de, Self::Cx>, + { + Err(self.cx().message(expecting::unsupported_type( + &expecting::Number, + ExpectingWrapper::new(&self), + ))) + } + /// Decode dynamically through a [`Visitor`]. #[inline] fn decode_any(self, visitor: V) -> Result::Error> diff --git a/crates/musli-core/src/de/entries_decoder.rs b/crates/musli-core/src/de/entries_decoder.rs index bd4ac8885..946b4d013 100644 --- a/crates/musli-core/src/de/entries_decoder.rs +++ b/crates/musli-core/src/de/entries_decoder.rs @@ -35,7 +35,7 @@ pub trait EntriesDecoder<'de>: Sized { /// Get a size hint for the size of the map being decoded. #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Any + SizeHint::any() } /// Try to return the decoder for the first value in the pair. diff --git a/crates/musli-core/src/de/entry_decoder.rs b/crates/musli-core/src/de/entry_decoder.rs index 5538ff73d..48c730218 100644 --- a/crates/musli-core/src/de/entry_decoder.rs +++ b/crates/musli-core/src/de/entry_decoder.rs @@ -26,7 +26,7 @@ pub trait EntryDecoder<'de> { /// Get a size hint for the size of the map being decoded. #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Any + SizeHint::any() } /// Return the decoder for the first value in the pair. diff --git a/crates/musli-core/src/de/map_decoder.rs b/crates/musli-core/src/de/map_decoder.rs index 86018c0bf..2bb52ad92 100644 --- a/crates/musli-core/src/de/map_decoder.rs +++ b/crates/musli-core/src/de/map_decoder.rs @@ -18,7 +18,7 @@ pub trait MapDecoder<'de>: Sized { /// Get a size hint of known remaining elements. #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Any + SizeHint::any() } /// Decode the next key. This returns `Ok(None)` where there are no more diff --git a/crates/musli-core/src/de/mod.rs b/crates/musli-core/src/de/mod.rs index 4644ba8d1..6f1092dff 100644 --- a/crates/musli-core/src/de/mod.rs +++ b/crates/musli-core/src/de/mod.rs @@ -22,24 +22,6 @@ //! } //! ``` -/// Derive which automatically implements the [`Decode` trait]. -/// -/// See the [`derives` module] for detailed documentation. -/// -/// [`derives` module]: -/// [`Decode` trait]: trait@Decode -/// -/// # Examples -/// -/// ``` -/// use musli::Decode; -/// -/// #[derive(Decode)] -/// struct MyType { -/// data: [u8; 128], -/// } -/// ``` -#[doc(inline)] pub use musli_macros::Decode; mod as_decoder; @@ -75,9 +57,6 @@ pub use self::entry_decoder::EntryDecoder; mod map_decoder; pub use self::map_decoder::MapDecoder; -mod number_visitor; -pub use self::number_visitor::NumberVisitor; - mod sequence_decoder; pub use self::sequence_decoder::SequenceDecoder; @@ -87,8 +66,8 @@ pub use self::size_hint::SizeHint; mod skip; pub use self::skip::Skip; -mod value_visitor; -pub use self::value_visitor::ValueVisitor; +mod unsized_visitor; +pub use self::unsized_visitor::UnsizedVisitor; mod variant_decoder; pub use self::variant_decoder::VariantDecoder; diff --git a/crates/musli-core/src/de/number_visitor.rs b/crates/musli-core/src/de/number_visitor.rs deleted file mode 100644 index d7ce37973..000000000 --- a/crates/musli-core/src/de/number_visitor.rs +++ /dev/null @@ -1,176 +0,0 @@ -use core::fmt; -use core::marker::PhantomData; - -use crate::expecting::{self, Expecting}; -use crate::Context; - -/// A visitor capable of processing arbitrary number values. -pub trait NumberVisitor<'de, C: ?Sized + Context>: Sized { - /// The output of the visitor. - type Ok; - - /// Format an error indicating what was expected by this visitor. - /// - /// Override to be more specific about the type that failed. - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; - - /// Visit `u8`. - #[inline] - fn visit_u8(self, cx: &C, _: u8) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Unsigned8, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `u16`. - #[inline] - fn visit_u16(self, cx: &C, _: u16) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Unsigned16, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `u32`. - #[inline] - fn visit_u32(self, cx: &C, _: u32) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Unsigned32, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `u64`. - #[inline] - fn visit_u64(self, cx: &C, _: u64) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Unsigned64, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `u128`. - #[inline] - fn visit_u128(self, cx: &C, _: u128) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Unsigned128, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `i8`. - #[inline] - fn visit_i8(self, cx: &C, _: i8) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Signed8, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `i16`. - #[inline] - fn visit_i16(self, cx: &C, _: i16) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Signed16, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `i32`. - #[inline] - fn visit_i32(self, cx: &C, _: i32) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Signed32, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `i64`. - #[inline] - fn visit_i64(self, cx: &C, _: i64) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Signed64, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `i128`. - #[inline] - fn visit_i128(self, cx: &C, _: i128) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Signed128, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `f32`. - #[inline] - fn visit_f32(self, cx: &C, _: f32) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Float32, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `f64`. - #[inline] - fn visit_f64(self, cx: &C, _: f64) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Float64, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `usize`. - #[inline] - fn visit_usize(self, cx: &C, _: usize) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Usize, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit `isize`. - #[inline] - fn visit_isize(self, cx: &C, _: isize) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Isize, - ExpectingWrapper::new(&self), - ))) - } - - /// Visit bytes constituting a raw number. - #[inline] - fn visit_bytes(self, cx: &C, _: &'de [u8]) -> Result { - Err(cx.message(expecting::bad_visitor_type( - &expecting::Number, - ExpectingWrapper::new(&self), - ))) - } -} - -#[repr(transparent)] -struct ExpectingWrapper<'a, T, C: ?Sized> { - inner: T, - _marker: PhantomData<&'a C>, -} - -impl<'a, T, C: ?Sized> ExpectingWrapper<'a, T, C> { - #[inline] - fn new(value: &T) -> &Self { - // SAFETY: `ExpectingWrapper` is repr(transparent) over `T`. - unsafe { &*(value as *const T as *const Self) } - } -} - -impl<'a, 'de, T, C> Expecting for ExpectingWrapper<'a, T, C> -where - C: ?Sized + Context, - T: NumberVisitor<'de, C>, -{ - #[inline] - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.expecting(f) - } -} diff --git a/crates/musli-core/src/de/sequence_decoder.rs b/crates/musli-core/src/de/sequence_decoder.rs index 09b84dac3..4a66b4e20 100644 --- a/crates/musli-core/src/de/sequence_decoder.rs +++ b/crates/musli-core/src/de/sequence_decoder.rs @@ -19,7 +19,7 @@ pub trait SequenceDecoder<'de>: Sized { /// Get a size hint of known remaining elements. #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Any + SizeHint::any() } /// Return decoder to decode the next element. diff --git a/crates/musli-core/src/de/size_hint.rs b/crates/musli-core/src/de/size_hint.rs index 9ddfb6725..5eea52f14 100644 --- a/crates/musli-core/src/de/size_hint.rs +++ b/crates/musli-core/src/de/size_hint.rs @@ -1,9 +1,7 @@ use core::fmt; -/// A length hint. #[derive(Default, Debug, Clone, Copy)] -#[non_exhaustive] -pub enum SizeHint { +enum SizeHintKind { /// The length isn't known. #[default] Any, @@ -11,49 +9,93 @@ pub enum SizeHint { Exact(usize), } +/// A length hint. +#[derive(Default, Debug, Clone, Copy)] +#[non_exhaustive] +pub struct SizeHint { + kind: SizeHintKind, +} + impl SizeHint { + /// Construct a size hint of unknown size. + /// + /// # Examples + /// + /// ``` + /// use musli::de::SizeHint; + /// + /// let hint = SizeHint::any(); + /// assert_eq!(hint.or_default(), 0); + /// ``` + #[inline] + pub const fn any() -> Self { + SizeHint { + kind: SizeHintKind::Any, + } + } + + /// Construct an exactly sized hint. + /// + /// # Examples + /// + /// ``` + /// use musli::de::SizeHint; + /// + /// let hint = SizeHint::exact(16); + /// assert_eq!(hint.or_default(), 16); + /// ``` + #[inline] + pub const fn exact(length: usize) -> Self { + SizeHint { + kind: SizeHintKind::Exact(length), + } + } + /// Get a size hint or a default value. + /// + /// # Examples + /// + /// ``` + /// use musli::de::SizeHint; + /// + /// let hint = SizeHint::any(); + /// assert_eq!(hint.or_default(), 0); + /// ``` pub fn or_default(self) -> usize { - match self { - SizeHint::Any => 0, - SizeHint::Exact(n) => n, + match self.kind { + SizeHintKind::Any => 0, + SizeHintKind::Exact(n) => n, } } } impl From> for SizeHint { fn from(value: Option) -> Self { - match value { - Some(n) => SizeHint::Exact(n), - None => SizeHint::Any, - } + let kind = match value { + Some(n) => SizeHintKind::Exact(n), + None => SizeHintKind::Any, + }; + + SizeHint { kind } } } impl fmt::Display for SizeHint { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SizeHint::Any => write!(f, "unknown length"), - SizeHint::Exact(length) => write!(f, "{length} items"), + match self.kind { + SizeHintKind::Any => write!(f, "unknown length"), + SizeHintKind::Exact(length) => write!(f, "{length} items"), } } } impl SizeHint { - /// Coerce into a size hint. - pub fn size_hint(self) -> usize { - match self { - SizeHint::Any => 0, - SizeHint::Exact(len) => len, - } - } - /// Coerce into an `Option`. pub fn into_option(self) -> Option { - match self { - SizeHint::Any => None, - SizeHint::Exact(len) => Some(len), + match self.kind { + SizeHintKind::Any => None, + SizeHintKind::Exact(len) => Some(len), } } } diff --git a/crates/musli-core/src/de/value_visitor.rs b/crates/musli-core/src/de/unsized_visitor.rs similarity index 69% rename from crates/musli-core/src/de/value_visitor.rs rename to crates/musli-core/src/de/unsized_visitor.rs index d1f37d394..1cbd7de3c 100644 --- a/crates/musli-core/src/de/value_visitor.rs +++ b/crates/musli-core/src/de/unsized_visitor.rs @@ -6,23 +6,27 @@ use crate::expecting::{self, Expecting}; use crate::no_std::ToOwned; use crate::Context; -/// A visitor for data where it might be possible to borrow it without copying -/// from the underlying [Decoder]. +/// A visitor for data where we might need to borrow without copying from the +/// underlying [`Decoder`]. /// -/// A visitor is required with [Decoder::decode_bytes] and -/// [Decoder::decode_string] because the caller doesn't know if the encoding +/// A visitor is needed with [`Decoder::decode_bytes`] and +/// [`Decoder::decode_string`] because the caller doesn't know if the encoding /// format is capable of producing references to the underlying data directly or -/// if it needs to be processed. +/// if it needs to be processed first. +/// +/// If all you want is to decode a value by reference, use the +/// [`Decoder::decode_unsized`] method. /// /// By requiring a visitor we ensure that the caller has to handle both -/// scenarios, even if one involves erroring. A type like -/// [Cow][std::borrow::Cow] is an example of a type which can comfortably handle -/// both. +/// scenarios, even if one involves erroring. A type like [Cow] is an example of +/// a type which can comfortably handle both. /// -/// [Decoder]: crate::de::Decoder -/// [Decoder::decode_bytes]: crate::de::Decoder::decode_bytes -/// [Decoder::decode_string]: crate::de::Decoder::decode_string -pub trait ValueVisitor<'de, C: ?Sized + Context, T>: Sized +/// [Cow]: std::borrow::Cow +/// [`Decoder`]: crate::de::Decoder +/// [`Decoder::decode_bytes`]: crate::de::Decoder::decode_bytes +/// [`Decoder::decode_string`]: crate::de::Decoder::decode_string +/// [`Decoder::decode_unsized`]: crate::de::Decoder::decode_unsized +pub trait UnsizedVisitor<'de, C: ?Sized + Context, T>: Sized where T: ?Sized + ToOwned, { @@ -73,7 +77,7 @@ impl<'a, T, C: ?Sized, U: ?Sized> ExpectingWrapper<'a, T, C, U> { impl<'a, 'de, T, C, U> Expecting for ExpectingWrapper<'a, T, C, U> where - T: ValueVisitor<'de, C, U>, + T: UnsizedVisitor<'de, C, U>, C: ?Sized + Context, U: ?Sized + ToOwned, { diff --git a/crates/musli-core/src/de/visitor.rs b/crates/musli-core/src/de/visitor.rs index aab487207..feda89030 100644 --- a/crates/musli-core/src/de/visitor.rs +++ b/crates/musli-core/src/de/visitor.rs @@ -4,9 +4,7 @@ use core::marker::PhantomData; use crate::expecting::{self, Expecting}; use crate::Context; -use super::{ - Decoder, MapDecoder, NumberVisitor, SequenceDecoder, SizeHint, ValueVisitor, VariantDecoder, -}; +use super::{Decoder, MapDecoder, SequenceDecoder, SizeHint, UnsizedVisitor, VariantDecoder}; /// Visitor capable of decoding any type into a value [`Visitor::Ok`]. /// @@ -17,11 +15,9 @@ pub trait Visitor<'de, C: ?Sized + Context>: Sized { /// The value produced by the visitor. type Ok; /// String decoder to use. - type String: ValueVisitor<'de, C, str, Ok = Self::Ok>; + type String: UnsizedVisitor<'de, C, str, Ok = Self::Ok>; /// Bytes decoder to use. - type Bytes: ValueVisitor<'de, C, [u8], Ok = Self::Ok>; - /// Number decoder to use. - type Number: NumberVisitor<'de, C, Ok = Self::Ok>; + type Bytes: UnsizedVisitor<'de, C, [u8], Ok = Self::Ok>; /// This is a type argument used to hint to any future implementor that they /// should be using the [`#[musli::visitor]`][musli::visitor] attribute @@ -33,11 +29,11 @@ pub trait Visitor<'de, C: ?Sized + Context>: Sized { /// expecting to decode some specific kind of value. fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; - /// Indicates that the visited type is a `unit`. + /// Indicates that the visited type is empty. #[inline] - fn visit_unit(self, cx: &C) -> Result { + fn visit_empty(self, cx: &C) -> Result { Err(cx.message(expecting::unsupported_type( - &expecting::Unit, + &expecting::Empty, ExpectingWrapper::new(&self), ))) } @@ -240,23 +236,26 @@ pub trait Visitor<'de, C: ?Sized + Context>: Sized { ))) } - /// Indicates that the visited type is a number. + /// Indicates that the visited type is a variant. #[inline] - fn visit_number(self, cx: &C) -> Result { + fn visit_variant(self, cx: &C, _: &mut D) -> Result + where + D: VariantDecoder<'de, Cx = C>, + { Err(cx.message(expecting::unsupported_type( - &expecting::Number, + &expecting::Variant, ExpectingWrapper::new(&self), ))) } - /// Indicates that the visited type is a variant. + /// Indicates that the encoding does not support dynamic types. #[inline] - fn visit_variant(self, cx: &C, _: &mut D) -> Result + fn visit_unknown(self, cx: &D::Cx, _: D) -> Result where - D: VariantDecoder<'de, Cx = C>, + D: Decoder<'de, Cx = C, Error = C::Error, Mode = C::Mode>, { Err(cx.message(expecting::unsupported_type( - &expecting::Variant, + &expecting::Any, ExpectingWrapper::new(&self), ))) } diff --git a/crates/musli-core/src/en/encoder.rs b/crates/musli-core/src/en/encoder.rs index d0938eee6..83a5b9985 100644 --- a/crates/musli-core/src/en/encoder.rs +++ b/crates/musli-core/src/en/encoder.rs @@ -100,14 +100,14 @@ pub trait Encoder: Sized { /// where /// E: Encoder, /// { - /// encoder.encode_unit() + /// encoder.encode_empty() /// } /// } /// ``` #[inline] - fn encode_unit(self) -> Result::Error> { + fn encode_empty(self) -> Result::Error> { Err(self.cx().message(expecting::unsupported_type( - &expecting::Unit, + &expecting::Empty, ExpectingWrapper::new(&self), ))) } @@ -1192,7 +1192,7 @@ pub trait Encoder: Sized { hint: &SequenceHint, ) -> Result::Error> { Err(self.cx().message(expecting::unsupported_type( - &expecting::Sequence, + &expecting::SequenceWith(hint.size_hint()), ExpectingWrapper::new(&self), ))) } @@ -1391,7 +1391,7 @@ pub trait Encoder: Sized { hint: &MapHint, ) -> Result::Error> { Err(self.cx().message(expecting::unsupported_type( - &expecting::MapPairs, + &expecting::MapWith(hint.size_hint()), ExpectingWrapper::new(&self), ))) } @@ -1580,7 +1580,7 @@ pub trait Encoder: Sized { { self.encode_variant_fn(|variant| { variant.encode_tag()?.encode(tag)?; - variant.encode_data()?.encode_unit()?; + variant.encode_data()?.encode_empty()?; Ok(()) }) } @@ -1694,7 +1694,7 @@ pub trait Encoder: Sized { T: ?Sized + Encode<::Mode>, { Err(self.cx().message(expecting::unsupported_type( - &expecting::StructVariant, + &expecting::MapVariant, ExpectingWrapper::new(&self), ))) } diff --git a/crates/musli-core/src/en/mod.rs b/crates/musli-core/src/en/mod.rs index f1376f859..d377a0de1 100644 --- a/crates/musli-core/src/en/mod.rs +++ b/crates/musli-core/src/en/mod.rs @@ -22,24 +22,6 @@ //! } //! ``` -/// Derive which automatically implements the [`Encode` trait]. -/// -/// See the [`derives` module] for detailed documentation. -/// -/// [`derives` module]: -/// [`Encode` trait]: trait@Encode -/// -/// # Examples -/// -/// ``` -/// use musli::Encode; -/// -/// #[derive(Encode)] -/// struct MyType { -/// data: [u8; 128], -/// } -/// ``` -#[doc(inline)] pub use musli_macros::Encode; mod encode; diff --git a/crates/musli-core/src/expecting.rs b/crates/musli-core/src/expecting.rs index 9ece3478e..484e106be 100644 --- a/crates/musli-core/src/expecting.rs +++ b/crates/musli-core/src/expecting.rs @@ -114,6 +114,9 @@ expect_with! { } expect! { + pub(crate) Any("a dynamic value"); + pub(crate) Empty("empty"); + pub(crate) Option("option"); pub(crate) Pack("pack"); pub(crate) Bool("boolean"); pub(crate) Char("character"); @@ -138,13 +141,10 @@ expect! { pub(crate) Array("array"); pub(crate) Map("map"); pub(crate) MapEntries("map entries"); - pub(crate) Option("option"); - pub(crate) Sequence("sequence"); - pub(crate) Unit("unit"); - pub(crate) UnsizedStruct("unsized struct"); - pub(crate) Variant("variant"); + pub(crate) UnsizedMap("unsized map"); + pub(crate) MapVariant("map variant"); + pub(crate) UnsizedSequence("unsized sequence"); pub(crate) SequenceVariant("sequence variant"); - pub(crate) StructVariant("struct variant"); + pub(crate) Variant("variant"); pub(crate) AnyValue("a value"); - pub(crate) MapPairs("map pairs"); } diff --git a/crates/musli-core/src/hint/map_hint.rs b/crates/musli-core/src/hint/map_hint.rs index 4832ad76c..8022d000a 100644 --- a/crates/musli-core/src/hint/map_hint.rs +++ b/crates/musli-core/src/hint/map_hint.rs @@ -1,3 +1,5 @@ +use crate::de::SizeHint; + /// A hint passed in when encoding a map. #[non_exhaustive] pub struct MapHint { @@ -21,4 +23,10 @@ impl MapHint { pub const fn with_size(size: usize) -> Self { Self { size } } + + /// Return the size hint that corresponds to this overall hint. + #[inline] + pub fn size_hint(&self) -> SizeHint { + SizeHint::exact(self.size) + } } diff --git a/crates/musli-core/src/hint/sequence_hint.rs b/crates/musli-core/src/hint/sequence_hint.rs index db91f24f5..a4f2289c8 100644 --- a/crates/musli-core/src/hint/sequence_hint.rs +++ b/crates/musli-core/src/hint/sequence_hint.rs @@ -1,3 +1,5 @@ +use crate::de::SizeHint; + /// A hint passed in when encoding a sequence. #[non_exhaustive] pub struct SequenceHint { @@ -21,4 +23,10 @@ impl SequenceHint { pub const fn with_size(size: usize) -> Self { Self { size } } + + /// Return the size hint that corresponds to this overall hint. + #[inline] + pub fn size_hint(&self) -> SizeHint { + SizeHint::exact(self.size) + } } diff --git a/crates/musli-core/src/impls/alloc.rs b/crates/musli-core/src/impls/alloc.rs index 7d7577989..dc829c032 100644 --- a/crates/musli-core/src/impls/alloc.rs +++ b/crates/musli-core/src/impls/alloc.rs @@ -21,7 +21,7 @@ use std::path::{Path, PathBuf}; use crate::de::{ Decode, DecodeBytes, DecodeTrace, Decoder, EntryDecoder, MapDecoder, SequenceDecoder, - ValueVisitor, + UnsizedVisitor, }; use crate::en::{ Encode, EncodeBytes, EncodePacked, EncodeTrace, Encoder, EntryEncoder, MapEncoder, @@ -52,7 +52,7 @@ impl<'de, M> Decode<'de, M> for String { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, str> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, str> for Visitor where C: ?Sized + Context, { @@ -134,7 +134,7 @@ macro_rules! cow { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, $source> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, $source> for Visitor where C: ?Sized + Context, { @@ -476,7 +476,7 @@ impl<'de, M> Decode<'de, M> for CString { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, { @@ -687,7 +687,7 @@ where struct Visitor; - impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, { @@ -794,7 +794,7 @@ impl<'de, M> DecodeBytes<'de, M> for Vec { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, { diff --git a/crates/musli-core/src/impls/mod.rs b/crates/musli-core/src/impls/mod.rs index f156ebb57..e25240742 100644 --- a/crates/musli-core/src/impls/mod.rs +++ b/crates/musli-core/src/impls/mod.rs @@ -16,7 +16,7 @@ use core::{fmt, marker}; use crate::de::{ Decode, DecodeBytes, DecodePacked, DecodeUnsized, DecodeUnsizedBytes, Decoder, SequenceDecoder, - ValueVisitor, VariantDecoder, + UnsizedVisitor, VariantDecoder, }; use crate::en::{Encode, EncodeBytes, EncodePacked, Encoder, SequenceEncoder, VariantEncoder}; use crate::hint::SequenceHint; @@ -37,7 +37,7 @@ impl Encode for () { where E: Encoder, { - encoder.encode_unit() + encoder.encode_empty() } } @@ -47,7 +47,7 @@ impl<'de, M> Decode<'de, M> for () { where D: Decoder<'de>, { - decoder.decode_unit() + decoder.decode_empty() } } @@ -57,7 +57,7 @@ impl Encode for marker::PhantomData { where E: Encoder, { - encoder.encode_unit() + encoder.encode_empty() } } @@ -67,7 +67,7 @@ impl<'de, M, T> Decode<'de, M> for marker::PhantomData { where D: Decoder<'de>, { - decoder.decode_unit()?; + decoder.decode_empty()?; Ok(marker::PhantomData) } } @@ -348,7 +348,7 @@ impl<'de, M> Decode<'de, M> for &'de str { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, str> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, str> for Visitor where C: ?Sized + Context, { @@ -378,7 +378,7 @@ impl<'de, M> DecodeUnsized<'de, M> for str { { struct Visitor(F); - impl<'de, C, F, O> ValueVisitor<'de, C, str> for Visitor + impl<'de, C, F, O> UnsizedVisitor<'de, C, str> for Visitor where C: ?Sized + Context, F: FnOnce(&str) -> Result, @@ -433,7 +433,7 @@ impl<'de, M> Decode<'de, M> for &'de [u8] { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, { @@ -463,7 +463,7 @@ impl<'de, M> DecodeUnsizedBytes<'de, M> for [u8] { { struct Visitor(F); - impl<'de, C, F, O> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C, F, O> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, F: FnOnce(&[u8]) -> Result, diff --git a/crates/musli-core/src/internal/size_hint.rs b/crates/musli-core/src/internal/size_hint.rs index be00ac927..5031da8bb 100644 --- a/crates/musli-core/src/internal/size_hint.rs +++ b/crates/musli-core/src/internal/size_hint.rs @@ -9,8 +9,5 @@ pub(crate) fn cautious(hint: S) -> usize where SizeHint: From, { - match SizeHint::from(hint) { - SizeHint::Any => 0, - SizeHint::Exact(n) => n.min(4096), - } + SizeHint::from(hint).or_default().min(4096) } diff --git a/crates/musli-core/src/never.rs b/crates/musli-core/src/never.rs index 74fb97a67..d73810a51 100644 --- a/crates/musli-core/src/never.rs +++ b/crates/musli-core/src/never.rs @@ -14,7 +14,7 @@ use crate::no_std::ToOwned; use crate::de::{ AsDecoder, Decode, DecodeUnsized, DecodeUnsizedBytes, Decoder, EntriesDecoder, EntryDecoder, - MapDecoder, NumberVisitor, SequenceDecoder, SizeHint, ValueVisitor, VariantDecoder, + MapDecoder, SequenceDecoder, SizeHint, UnsizedVisitor, VariantDecoder, }; use crate::en::{ Encode, Encoder, EntriesEncoder, EntryEncoder, MapEncoder, SequenceEncoder, VariantEncoder, @@ -299,15 +299,7 @@ impl Encoder for Never { } } -impl<'de, O, C: ?Sized + Context> NumberVisitor<'de, C> for Never { - type Ok = O; - - fn expecting(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - match self._never {} - } -} - -impl<'de, C, O: 'static, T> ValueVisitor<'de, C, T> for Never +impl<'de, C, O: 'static, T> UnsizedVisitor<'de, C, T> for Never where C: ?Sized + Context, T: ?Sized + ToOwned, diff --git a/crates/musli-core/src/no_std.rs b/crates/musli-core/src/no_std.rs index d84669911..3d8e1b3f1 100644 --- a/crates/musli-core/src/no_std.rs +++ b/crates/musli-core/src/no_std.rs @@ -1,13 +1,13 @@ -//! Trait fills. -//! -//! This will replace the following traits in `#[no_std]` environments: +//! Trait fills for `#[no_std]` environments. //! //! * [`ToOwned`] - if the `alloc` feature is enabled, this is an alias for //! `alloc::borrow::ToOwned`. //! * [`Error`] - if the `std` feature is enabled, this is an alias for //! `std::error::Error`. If the `std` feature is disabled, this is a trait //! which is implemented for everything that implements [`Debug`] and -//! [`Display`]. +//! [`Display`]. Note that this means that enabling the `std` feature might +//! cause code that is designed carelessly to break due to no longer +//! implementing the trait. //! //! [`Debug`]: core::fmt::Debug //! [`Display`]: core::fmt::Display diff --git a/crates/musli-macros/Cargo.toml b/crates/musli-macros/Cargo.toml index b6c44e66c..0abd4243b 100644 --- a/crates/musli-macros/Cargo.toml +++ b/crates/musli-macros/Cargo.toml @@ -26,3 +26,6 @@ verbose = [] proc-macro2 = "1.0.79" quote = "1.0.35" syn = { version = "2.0.55", features = ["full", "extra-traits"] } + +[dev-dependencies] +musli = { version = "=0.0.120", path = "../musli" } diff --git a/crates/musli-macros/src/lib.rs b/crates/musli-macros/src/lib.rs index 1e443f0f1..8455fcc77 100644 --- a/crates/musli-macros/src/lib.rs +++ b/crates/musli-macros/src/lib.rs @@ -22,11 +22,45 @@ use proc_macro::TokenStream; const CRATE_DEFAULT: &str = "musli"; +/// Derive which automatically implements the [`Encode` trait]. +/// +/// See the [`derives` module] for detailed documentation. +/// +/// [`derives` module]: +/// [`Encode` trait]: +/// +/// # Examples +/// +/// ``` +/// use musli::Encode; +/// +/// #[derive(Encode)] +/// struct MyType { +/// data: [u8; 128], +/// } +/// ``` #[proc_macro_derive(Encode, attributes(musli))] pub fn musli_derive_encode(input: TokenStream) -> TokenStream { derive_encode(input, CRATE_DEFAULT) } +/// Derive which automatically implements the [`Decode` trait]. +/// +/// See the [`derives` module] for detailed documentation. +/// +/// [`derives` module]: +/// [`Decode` trait]: +/// +/// # Examples +/// +/// ``` +/// use musli::Decode; +/// +/// #[derive(Decode)] +/// struct MyType { +/// data: [u8; 128], +/// } +/// ``` #[proc_macro_derive(Decode, attributes(musli))] pub fn musli_derive_decode(input: TokenStream) -> TokenStream { derive_decode(input, CRATE_DEFAULT) diff --git a/crates/musli-macros/src/types.rs b/crates/musli-macros/src/types.rs index d5bc10f31..9a304bcc9 100644 --- a/crates/musli-macros/src/types.rs +++ b/crates/musli-macros/src/types.rs @@ -72,7 +72,6 @@ pub(super) const DECODER_FNS: &[(&str, Fn)] = &[ pub(super) const VISITOR_TYPES: &[(&str, Extra)] = &[ ("String", Extra::Visitor(Ty::Str)), ("Bytes", Extra::Visitor(Ty::Bytes)), - ("Number", Extra::None), ]; #[derive(Clone, Copy)] diff --git a/crates/musli/README.md b/crates/musli/README.md index 2439c4119..0e1ac2e81 100644 --- a/crates/musli/README.md +++ b/crates/musli/README.md @@ -23,6 +23,7 @@ low-level refreshingly simple [zero-copy serialization][zerocopy]. ## Overview * See [`derives`] to learn how to implement [`Encode`] and [`Decode`]. +* See [`data_model`] to learn about the abstract data model of Müsli. * See [benchmarks] and [size comparisons] to learn about the performance of this framework. * See [`tests`] to learn how this library is tested. @@ -117,47 +118,38 @@ fn without_musli(storage: &Storage) -> Result<[u8; 8]> { ## Müsli is different from [`serde`] -* We make use of GATs to provide tighter abstractions. GATs were not - available when serde was designed. -* When decoding or encoding we operate by the principle that most things - return either return a [`Decoder`] or [`Encoder`]. This means for example - that field names are not restricted to be strings or indexes, but can be - renamed to [completely arbitrary types][musli-name-type]. -* We make less use of the Visitor pattern in certain instances where it's - deemed unnecessary, such as [when decoding collections]. The result is - usually cleaner decode implementations like below. -* We make use of [*moded encoding*](#Modes) allowing the same struct to be - encoded in many different ways. -* We support [detailed tracing] when decoding for rich diagnostics. -* Müsli was designed to support [no-std and no-alloc] environments from the - ground up without compromising on features using a safe and efficient - [scoped allocations]. +**Müsli's data model does not speak Rust**. There are no +`serialize_struct_variant` methods which provides metadata about the type +being serialized. The [`Encoder`] and [`Decoder`] traits are agnostic on +this. Compatibility with Rust types is entirely handled using the [`Encode`] +and [`Decode`] derives in combination with [modes](#Modes). -```rust -use musli::Context; -use musli::de::{Decode, Decoder, SequenceDecoder}; +**We use GATs** to provide easier to use abstractions. GATs were not +available when serde was designed. -struct MyType { - data: Vec, -} +**Everything is a [`Decoder`] or [`Encoder`]**. Field names are therefore +not limited to be strings or indexes, but can be named to [arbitrary +types][musli-name-type] if needed. -impl<'de, M> Decode<'de, M> for MyType { - fn decode(cx: &D::Cx, decoder: D) -> Result - where - D: Decoder<'de, Mode = M>, - { - decoder.decode_sequence(|seq| { - let mut data = Vec::with_capacity(seq.size_hint().or_default()); - - while let Some(decoder) = seq.try_decode_next()? { - data.push(decoder.decode()?); - } - - Ok(Self { data }) - }) - } -} -``` +**Visitor are only used when needed**. `serde` [completely uses visitors] +when deserializing and the corresponding method is treated as a "hint" to +the underlying format. The deserializer is then free to call any method on +the visitor depending on what the underlying format actually contains. In +Müsli, we swap this around. If the caller wants to decode an arbitrary type +it calls [`decode_any`]. The format can then either signal the appropriate +underlying type or call [`Visitor::visit_unknown`] telling the implementer +that it does not have access to type information. + +**We've invented [*moded encoding*](#Modes)** allowing the same Rust types +to be encoded in many different ways with much greater control over how +things encoded. By default we include the [`Binary`] and [`Text`] modes +providing sensible defaults for binary and text-based formats. + +**Müsli fully supports [no-std and no-alloc]** from the ground up without +compromising on features using safe and efficient [scoped allocations]. + +**We support [detailed tracing]** when decoding for much improved +diagnostics of *where* something went wrong.
@@ -360,6 +352,8 @@ safety, extensive testing and fuzzing is performed using `miri`. See [`Binary`]: [`bincode`]: +[`data_model`]: +[`decode_any`]: https://docs.rs/musli/latest/musli/trait.Decoder.html#method.decode_any [`Decode`]: [`Decoder`]: [`derives`]: @@ -375,12 +369,14 @@ safety, extensive testing and fuzzing is performed using `miri`. See [`serde`]: [`simdutf8`]: [`tests`]: +[`Text`]: +[`Visitor::visit_unknown`]: https://docs.rs/musli/latest/musli/de/trait.Visitor.html#method.visit_unknown [benchmarks]: [bit packing]: +[completely uses visitors]: https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_u32 [detailed tracing]: [musli-name-type]: [no-std and no-alloc]: [scoped allocations]: [size comparisons]: -[when decoding collections]: [zerocopy]: diff --git a/crates/musli/src/context/access.rs b/crates/musli/src/context/access.rs index 7cbfc39ed..25859bf43 100644 --- a/crates/musli/src/context/access.rs +++ b/crates/musli/src/context/access.rs @@ -21,11 +21,10 @@ impl Access { } if state == isize::MIN { - crate::system::abort(); + crate::no_std::abort("access state overflowed"); } self.state.set(state - 1); - Shared { access: self } } @@ -38,7 +37,7 @@ impl Access { } if state == isize::MIN { - crate::system::abort(); + crate::no_std::abort("access state overflowed"); } self.state.set(1); diff --git a/crates/musli/src/de.rs b/crates/musli/src/de.rs index f05b48484..d97d55b47 100644 --- a/crates/musli/src/de.rs +++ b/crates/musli/src/de.rs @@ -19,6 +19,6 @@ #[doc(inline)] pub use musli_core::de::{ AsDecoder, Decode, DecodeBytes, DecodeOwned, DecodePacked, DecodeTrace, DecodeUnsized, - DecodeUnsizedBytes, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, NumberVisitor, - SequenceDecoder, SizeHint, Skip, ValueVisitor, VariantDecoder, Visitor, + DecodeUnsizedBytes, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, SequenceDecoder, + SizeHint, Skip, UnsizedVisitor, VariantDecoder, Visitor, }; diff --git a/crates/musli/src/descriptive/de.rs b/crates/musli/src/descriptive/de.rs index 0841339e8..c96051846 100644 --- a/crates/musli/src/descriptive/de.rs +++ b/crates/musli/src/descriptive/de.rs @@ -5,8 +5,8 @@ use core::mem::take; use alloc::vec::Vec; use crate::de::{ - Decode, DecodeUnsized, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, NumberVisitor, - SequenceDecoder, SizeHint, Skip, ValueVisitor, VariantDecoder, Visitor, + Decode, DecodeUnsized, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, SequenceDecoder, + SizeHint, Skip, UnsizedVisitor, VariantDecoder, Visitor, }; use crate::hint::{MapHint, SequenceHint}; use crate::int::continuation as c; @@ -290,7 +290,7 @@ where } #[inline] - fn decode_unit(self) -> Result<(), C::Error> { + fn decode_empty(self) -> Result<(), C::Error> { self.skip() } @@ -327,7 +327,7 @@ where #[inline] fn decode_bytes(mut self, visitor: V) -> Result where - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { let pos = self.cx.mark(); let len = self.decode_prefix(Kind::Bytes, pos)?; @@ -337,14 +337,14 @@ where #[inline] fn decode_string(mut self, visitor: V) -> Result where - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { struct Visitor(V); - impl<'de, C, V> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C, V> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { type Ok = V::Ok; @@ -422,7 +422,7 @@ where #[inline] fn decode_number(mut self, visitor: V) -> Result where - V: NumberVisitor<'de, C>, + V: Visitor<'de, C>, { let cx = self.cx; let tag = Tag::from_byte(self.reader.read_byte(cx)?); @@ -726,7 +726,7 @@ where Kind::Bytes => { let hint = tag .data() - .map(|d| SizeHint::Exact(d as usize)) + .map(|d| SizeHint::exact(d as usize)) .unwrap_or_default(); let visitor = visitor.visit_bytes(cx, hint)?; self.decode_bytes(visitor) @@ -734,7 +734,7 @@ where Kind::String => { let hint = tag .data() - .map(|d| SizeHint::Exact(d as usize)) + .map(|d| SizeHint::exact(d as usize)) .unwrap_or_default(); let visitor = visitor.visit_string(cx, hint)?; self.decode_string(visitor) @@ -754,8 +754,8 @@ where visitor.visit_char(cx, value) } Mark::Unit => { - self.decode_unit()?; - visitor.visit_unit(cx) + self.decode_empty()?; + visitor.visit_empty(cx) } mark => Err(cx.message(format_args!("Unsupported mark {mark:?}"))), }, @@ -793,7 +793,7 @@ where #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Exact(self.remaining) + SizeHint::exact(self.remaining) } #[inline] @@ -833,7 +833,7 @@ where #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Exact(self.remaining) + SizeHint::exact(self.remaining) } #[inline] diff --git a/crates/musli/src/descriptive/en.rs b/crates/musli/src/descriptive/en.rs index 75035d9cd..9edbe6896 100644 --- a/crates/musli/src/descriptive/en.rs +++ b/crates/musli/src/descriptive/en.rs @@ -96,7 +96,7 @@ where } #[inline] - fn encode_unit(mut self) -> Result { + fn encode_empty(mut self) -> Result { self.writer .write_byte(self.cx, Tag::from_mark(Mark::Unit).byte())?; Ok(()) @@ -290,7 +290,7 @@ where { let mut variant = self.encode_variant()?; variant.encode_tag()?.encode(tag)?; - variant.encode_data()?.encode_unit()?; + variant.encode_data()?.encode_empty()?; VariantEncoder::finish_variant(variant)?; Ok(()) } diff --git a/crates/musli/src/descriptive/mod.rs b/crates/musli/src/descriptive/mod.rs index a1c9fd1e6..6356795c9 100644 --- a/crates/musli/src/descriptive/mod.rs +++ b/crates/musli/src/descriptive/mod.rs @@ -116,7 +116,7 @@ pub use self::encoding::to_vec; #[doc(inline)] pub use self::encoding::to_writer; #[doc(inline)] -pub use self::encoding::{decode, encode, from_slice, to_fixed_bytes, Encoding, OPTIONS}; +pub use self::encoding::{decode, encode, from_slice, to_fixed_bytes, Encoding, DEFAULT, OPTIONS}; #[doc(inline)] pub use self::error::Error; diff --git a/crates/musli/src/descriptive/tag.rs b/crates/musli/src/descriptive/tag.rs index 5526023b5..d6a78e97d 100644 --- a/crates/musli/src/descriptive/tag.rs +++ b/crates/musli/src/descriptive/tag.rs @@ -168,10 +168,12 @@ pub(crate) struct Tag { impl Tag { /// Construct a new tag through an unchecked constructor. /// - /// `data` must not be equal to or larger than [MAX_INLINE_LEN], or else it - /// could corrupt the payload. + /// The `data` argument must fit within the numerical bounds specified by + /// [`DATA_MASK`]. #[inline] pub(crate) const fn new(kind: Kind, data: u8) -> Self { + debug_assert!(data <= DATA_MASK, "Data must fit in DATA_MASK"); + Self { repr: kind as u8 | data, } diff --git a/crates/musli/src/help/data_model.rs b/crates/musli/src/help/data_model.rs index 394f58039..47d461547 100644 --- a/crates/musli/src/help/data_model.rs +++ b/crates/musli/src/help/data_model.rs @@ -1,17 +1,21 @@ //! The data model of Müsli. //! -//! Müsli supports the following types natively: +//! Müsli supports the following fundamental types: +//! +//! * Empty[^empty]. //! * Boolean values. -//! * Integers (8 to 128 bits). -//! * Floats (32 and 64 bits). -//! * Optional values. -//! * Strings (a sequence of bytes known to be a valid utf-8 string). -//! * Bytes (a sequence of raw bytes). -//! * Sequences. -//! * Maps or a sequence of pairs. There is no restriction on the key, and they -//! can contain duplicates. -//! * A variant, which is a simple pair of a key and a value, where the key is -//! the discriminant identifying the type. +//! * Unsigned integers (corresponding to [u8], [u16], [u32], [u64], and +//! [u128]). +//! * Signed integers (corresponding to [i8], [i16], [i32], [i64], and [i128]). +//! * Floats (corresponding to [f32] and [f64]). +//! * Optional values[^option]. +//! * Bytes, a raw byte sequence. +//! * Strings, a byte sequence known to be a valid utf-8 string. +//! * Sequences[^container]. +//! * Maps[^container]. There is no restriction on the key, and they can contain +//! duplicates. +//! * A variant[^container], which is a simple kind of container containing a +//! key and a value. The key is the discriminant identifying the variant. //! //! These are used as the basis to serialize any Rust type. //! @@ -25,4 +29,24 @@ //! //! To control the exact behavior of serialization, see the [`derives`] section. //! +//! [^empty]: Empty values serve the purpose of acting as placeholder for things +//! which have no value, such as the empty tuple `()` or `PhantomData`. +//! Encoders are free to treat them however they want to. For descriptive +//! encoders where it's possible, it's typical for empty values to be +//! skipped. +//! +//! [^option]: This directly corresponds to the `Option` type in Rust. While +//! many formats internally handles optionality since it is a requirement to +//! skip over unknown fields, this type is given special treatment to ensure +//! that formats which are not descriptive can handle them. Without this, it +//! would be impossible for the non-packed [`storage`] format to provide +//! partial upgrade safety. +//! +//! [^container]: There is no particular restriction that containers must +//! contain uniform types. However, this is typically enforced by the types +//! deriving [`Encode`] and [`Decode`] in Rust. +//! +//! [`storage`]: crate::storage //! [`derives`]: super::derives +//! [`Encode`]: crate::Encode +//! [`Decode`]: crate::Decode diff --git a/crates/musli/src/json/de/key_decoder.rs b/crates/musli/src/json/de/key_decoder.rs index 29bd801d4..b29ae55c7 100644 --- a/crates/musli/src/json/de/key_decoder.rs +++ b/crates/musli/src/json/de/key_decoder.rs @@ -1,6 +1,6 @@ use core::fmt; -use crate::de::{Decode, DecodeUnsized, Decoder, SizeHint, Skip, ValueVisitor, Visitor}; +use crate::de::{Decode, DecodeUnsized, Decoder, SizeHint, Skip, UnsizedVisitor, Visitor}; use crate::Context; use super::super::parser::{Parser, Token}; @@ -26,7 +26,7 @@ where #[inline] fn decode_escaped_bytes(mut self, visitor: V) -> Result where - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { let Some(mut scratch) = self.cx.alloc() else { return Err(self.cx.message("Failed to allocate scratch buffer")); @@ -159,7 +159,7 @@ where #[inline] fn decode_string(self, visitor: V) -> Result where - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { JsonDecoder::new(self.cx, self.parser).decode_string(visitor) } @@ -171,13 +171,10 @@ where { match self.parser.peek(self.cx)? { Token::String => { - let visitor = visitor.visit_string(self.cx, SizeHint::Any)?; + let visitor = visitor.visit_string(self.cx, SizeHint::any())?; self.decode_string(visitor) } - Token::Number => { - let visitor = visitor.visit_number(self.cx)?; - self.decode_number(visitor) - } + Token::Number => self.decode_number(visitor), token => Err(self .cx .message(format_args!("Unsupported key type {token:?}"))), diff --git a/crates/musli/src/json/de/key_signed_visitor.rs b/crates/musli/src/json/de/key_signed_visitor.rs index facca3a64..719d16835 100644 --- a/crates/musli/src/json/de/key_signed_visitor.rs +++ b/crates/musli/src/json/de/key_signed_visitor.rs @@ -1,7 +1,7 @@ use core::fmt; use core::marker; -use crate::de::ValueVisitor; +use crate::de::UnsizedVisitor; use crate::json::parser::integer::Signed; use crate::json::parser::SliceParser; use crate::Context; @@ -20,7 +20,7 @@ impl KeySignedVisitor { } } -impl<'de, C: ?Sized + Context, T> ValueVisitor<'de, C, [u8]> for KeySignedVisitor +impl<'de, C: ?Sized + Context, T> UnsizedVisitor<'de, C, [u8]> for KeySignedVisitor where T: Signed, { diff --git a/crates/musli/src/json/de/key_unsigned_visitor.rs b/crates/musli/src/json/de/key_unsigned_visitor.rs index c84b10d71..8f79a73e5 100644 --- a/crates/musli/src/json/de/key_unsigned_visitor.rs +++ b/crates/musli/src/json/de/key_unsigned_visitor.rs @@ -1,7 +1,7 @@ use core::fmt; use core::marker; -use crate::de::ValueVisitor; +use crate::de::UnsizedVisitor; use crate::json::parser::integer::Unsigned; use crate::json::parser::SliceParser; use crate::Context; @@ -20,7 +20,7 @@ impl KeyUnsignedVisitor { } } -impl<'de, C, T> ValueVisitor<'de, C, [u8]> for KeyUnsignedVisitor +impl<'de, C, T> UnsizedVisitor<'de, C, [u8]> for KeyUnsignedVisitor where C: ?Sized + Context, T: Unsigned, diff --git a/crates/musli/src/json/de/mod.rs b/crates/musli/src/json/de/mod.rs index 4ca11b115..8e2f78afe 100644 --- a/crates/musli/src/json/de/mod.rs +++ b/crates/musli/src/json/de/mod.rs @@ -26,8 +26,7 @@ use core::str; use alloc::vec::Vec; use crate::de::{ - Decode, DecodeUnsized, Decoder, NumberVisitor, SequenceDecoder, SizeHint, Skip, ValueVisitor, - Visitor, + Decode, DecodeUnsized, Decoder, SequenceDecoder, SizeHint, Skip, UnsizedVisitor, Visitor, }; use crate::hint::{MapHint, SequenceHint}; #[cfg(feature = "value")] @@ -183,7 +182,7 @@ where } #[inline] - fn decode_unit(self) -> Result<(), C::Error> { + fn decode_empty(self) -> Result<(), C::Error> { self.skip() } @@ -328,19 +327,11 @@ where }) } - #[inline] - fn decode_number(mut self, visitor: V) -> Result - where - V: NumberVisitor<'de, C>, - { - self.parser.parse_number(self.cx, visitor) - } - #[cfg(feature = "alloc")] #[inline] fn decode_bytes(self, visitor: V) -> Result where - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { let cx = self.cx; @@ -358,7 +349,7 @@ where #[inline] fn decode_string(mut self, visitor: V) -> Result where - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { let Some(mut scratch) = self.cx.alloc() else { return Err(self.cx.message("Failed to allocate scratch buffer")); @@ -451,6 +442,14 @@ where Ok(output) } + #[inline] + fn decode_number(mut self, visitor: V) -> Result + where + V: Visitor<'de, C>, + { + self.parser.parse_number(self.cx, visitor) + } + #[inline] fn decode_any(mut self, visitor: V) -> Result where @@ -464,16 +463,13 @@ where self.decode_sequence(|decoder| visitor.visit_sequence(cx, decoder)) } Token::String => { - let visitor = visitor.visit_string(cx, SizeHint::Any)?; + let visitor = visitor.visit_string(cx, SizeHint::any())?; self.decode_string(visitor) } - Token::Number => { - let visitor = visitor.visit_number(cx)?; - self.decode_number(visitor) - } + Token::Number => self.decode_number(visitor), Token::Null => { self.parse_null()?; - visitor.visit_unit(cx) + visitor.visit_empty(cx) } Token::True => { self.parse_true()?; diff --git a/crates/musli/src/json/en/mod.rs b/crates/musli/src/json/en/mod.rs index c9e9dd72a..1356caed1 100644 --- a/crates/musli/src/json/en/mod.rs +++ b/crates/musli/src/json/en/mod.rs @@ -80,7 +80,7 @@ where } #[inline] - fn encode_unit(mut self) -> Result { + fn encode_empty(mut self) -> Result { self.writer.write_bytes(self.cx, b"null") } @@ -263,7 +263,7 @@ where #[inline] fn encode_none(self) -> Result { - self.encode_unit() + self.encode_empty() } #[inline] diff --git a/crates/musli/src/json/mod.rs b/crates/musli/src/json/mod.rs index 0e489a9f1..d737db338 100644 --- a/crates/musli/src/json/mod.rs +++ b/crates/musli/src/json/mod.rs @@ -59,7 +59,7 @@ pub type Result = core::result::Result; #[doc(inline)] pub use self::encoding::to_writer; #[doc(inline)] -pub use self::encoding::{decode, encode, from_slice, from_str, to_fixed_bytes, Encoding}; +pub use self::encoding::{decode, encode, from_slice, from_str, to_fixed_bytes, Encoding, DEFAULT}; #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[doc(inline)] diff --git a/crates/musli/src/json/parser/parser.rs b/crates/musli/src/json/parser/parser.rs index f82dfc88c..4f1b600f6 100644 --- a/crates/musli/src/json/parser/parser.rs +++ b/crates/musli/src/json/parser/parser.rs @@ -1,4 +1,4 @@ -use crate::de::NumberVisitor; +use crate::de::Visitor; use crate::json::parser::integer::decode_signed_full; use crate::json::parser::{string, StringReference, Token}; use crate::{Buf, Context}; @@ -158,12 +158,12 @@ pub trait Parser<'de>: private::Sealed { } /// Parse an unknown number and try to coerce it into the best fit type - /// through [NumberVisitor]. + /// through [Visitor]. #[doc(hidden)] fn parse_number(&mut self, cx: &C, visitor: V) -> Result where C: ?Sized + Context, - V: NumberVisitor<'de, C>, + V: Visitor<'de, C>, { let signed = decode_signed_full::(cx, self)?; diff --git a/crates/musli/src/lib.rs b/crates/musli/src/lib.rs index e4c46cb5f..98dbafb41 100644 --- a/crates/musli/src/lib.rs +++ b/crates/musli/src/lib.rs @@ -20,6 +20,7 @@ //! ## Overview //! //! * See [`derives`] to learn how to implement [`Encode`] and [`Decode`]. +//! * See [`data_model`] to learn about the abstract data model of Müsli. //! * See [benchmarks] and [size comparisons] to learn about the performance of //! this framework. //! * See [`tests`] to learn how this library is tested. @@ -119,47 +120,38 @@ //! //! ## Müsli is different from [`serde`] //! -//! * We make use of GATs to provide tighter abstractions. GATs were not -//! available when serde was designed. -//! * When decoding or encoding we operate by the principle that most things -//! return either return a [`Decoder`] or [`Encoder`]. This means for example -//! that field names are not restricted to be strings or indexes, but can be -//! renamed to [completely arbitrary types][musli-name-type]. -//! * We make less use of the Visitor pattern in certain instances where it's -//! deemed unnecessary, such as [when decoding collections]. The result is -//! usually cleaner decode implementations like below. -//! * We make use of [*moded encoding*](#Modes) allowing the same struct to be -//! encoded in many different ways. -//! * We support [detailed tracing] when decoding for rich diagnostics. -//! * Müsli was designed to support [no-std and no-alloc] environments from the -//! ground up without compromising on features using a safe and efficient -//! [scoped allocations]. +//! **Müsli's data model does not speak Rust**. There are no +//! `serialize_struct_variant` methods which provides metadata about the type +//! being serialized. The [`Encoder`] and [`Decoder`] traits are agnostic on +//! this. Compatibility with Rust types is entirely handled using the [`Encode`] +//! and [`Decode`] derives in combination with [modes](#Modes). //! -//! ``` -//! use musli::Context; -//! use musli::de::{Decode, Decoder, SequenceDecoder}; +//! **We use GATs** to provide easier to use abstractions. GATs were not +//! available when serde was designed. //! -//! struct MyType { -//! data: Vec, -//! } +//! **Everything is a [`Decoder`] or [`Encoder`]**. Field names are therefore +//! not limited to be strings or indexes, but can be named to [arbitrary +//! types][musli-name-type] if needed. //! -//! impl<'de, M> Decode<'de, M> for MyType { -//! fn decode(cx: &D::Cx, decoder: D) -> Result -//! where -//! D: Decoder<'de, Mode = M>, -//! { -//! decoder.decode_sequence(|seq| { -//! let mut data = Vec::with_capacity(seq.size_hint().or_default()); -//! -//! while let Some(decoder) = seq.try_decode_next()? { -//! data.push(decoder.decode()?); -//! } -//! -//! Ok(Self { data }) -//! }) -//! } -//! } -//! ``` +//! **Visitor are only used when needed**. `serde` [completely uses visitors] +//! when deserializing and the corresponding method is treated as a "hint" to +//! the underlying format. The deserializer is then free to call any method on +//! the visitor depending on what the underlying format actually contains. In +//! Müsli, we swap this around. If the caller wants to decode an arbitrary type +//! it calls [`decode_any`]. The format can then either signal the appropriate +//! underlying type or call [`Visitor::visit_unknown`] telling the implementer +//! that it does not have access to type information. +//! +//! **We've invented [*moded encoding*](#Modes)** allowing the same Rust types +//! to be encoded in many different ways with much greater control over how +//! things encoded. By default we include the [`Binary`] and [`Text`] modes +//! providing sensible defaults for binary and text-based formats. +//! +//! **Müsli fully supports [no-std and no-alloc]** from the ground up without +//! compromising on features using safe and efficient [scoped allocations]. +//! +//! **We support [detailed tracing]** when decoding for much improved +//! diagnostics of *where* something went wrong. //! //!
//! @@ -371,6 +363,8 @@ //! //! [`Binary`]: //! [`bincode`]: +//! [`data_model`]: +//! [`decode_any`]: https://docs.rs/musli/latest/musli/trait.Decoder.html#method.decode_any //! [`Decode`]: //! [`Decoder`]: //! [`derives`]: @@ -386,14 +380,16 @@ //! [`serde`]: //! [`simdutf8`]: //! [`tests`]: +//! [`Text`]: +//! [`Visitor::visit_unknown`]: https://docs.rs/musli/latest/musli/de/trait.Visitor.html#method.visit_unknown //! [benchmarks]: //! [bit packing]: +//! [completely uses visitors]: https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_u32 //! [detailed tracing]: //! [musli-name-type]: //! [no-std and no-alloc]: //! [scoped allocations]: //! [size comparisons]: -//! [when decoding collections]: //! [zerocopy]: #![deny(missing_docs)] @@ -425,8 +421,6 @@ pub mod en; pub use musli_core::hint; #[doc(inline)] pub use musli_core::mode; -#[doc(inline)] -pub use musli_core::no_std; /// This is an attribute macro that must be used when implementing a /// [`Encoder`]. @@ -598,9 +592,7 @@ pub mod writer; #[doc(inline)] pub use self::writer::Writer; +pub mod no_std; + mod int; mod str; - -#[cfg_attr(feature = "std", path = "system/std.rs")] -#[cfg_attr(not(feature = "std"), path = "system/no_std.rs")] -mod system; diff --git a/crates/musli/src/macros.rs b/crates/musli/src/macros.rs index 31cae9f9a..f741c5da0 100644 --- a/crates/musli/src/macros.rs +++ b/crates/musli/src/macros.rs @@ -439,6 +439,7 @@ macro_rules! rt { /// This is used to test when there is a decode assymmetry, such as the decoded /// value does not match the encoded one due to things such as skipped fields. #[cfg(feature = "test")] +#[doc(hidden)] #[macro_export] macro_rules! assert_decode_eq { ($what:ident, $expr:expr, $expected:expr $(, $($extra:tt)*)?) => {{ @@ -457,6 +458,7 @@ macro_rules! assert_decode_eq { #[cfg(feature = "test")] #[macro_export] +#[doc(hidden)] macro_rules! extra { ($expr:expr $(,)?) => {}; @@ -474,6 +476,7 @@ macro_rules! extra { } #[cfg(feature = "test")] +#[doc(hidden)] #[macro_export] macro_rules! test_matrix { (full, $call:path) => { diff --git a/crates/musli/src/no_std.rs b/crates/musli/src/no_std.rs new file mode 100644 index 000000000..ddd27808d --- /dev/null +++ b/crates/musli/src/no_std.rs @@ -0,0 +1,44 @@ +//! Trait fills for `#[no_std]` environments. +//! +//! * [`ToOwned`] - if the `alloc` feature is enabled, this is an alias for +//! `alloc::borrow::ToOwned`. +//! * [`Error`] - if the `std` feature is enabled, this is an alias for +//! `std::error::Error`. If the `std` feature is disabled, this is a trait +//! which is implemented for everything that implements [`Debug`] and +//! [`Display`]. Note that this means that enabling the `std` feature might +//! cause code that is designed carelessly to break due to no longer +//! implementing the trait. +//! +//! [`Debug`]: core::fmt::Debug +//! [`Display`]: core::fmt::Display + +#[doc(inline)] +pub use musli_core::no_std::{Error, ToOwned}; + +/// A somewhat portable, but also noisy abort implementation for no_std +/// environments. +/// +/// While this should ultimately cause the process to abort, it will first cause +/// the process to panic and report it through the panic hook. +#[cold] +#[cfg(not(feature = "std"))] +pub(crate) fn abort(s: &'static str) -> ! { + struct Abort; + + // A panic during an unwinding drop leads to an abort. + impl Drop for Abort { + #[inline(always)] + fn drop(&mut self) { + panic!() + } + } + + let _a = Abort; + panic!("{s}") +} + +#[cfg(feature = "std")] +#[cold] +pub(crate) fn abort(_: &'static str) -> ! { + ::std::process::abort(); +} diff --git a/crates/musli/src/reader.rs b/crates/musli/src/reader.rs index 655e82878..b6f03ec1c 100644 --- a/crates/musli/src/reader.rs +++ b/crates/musli/src/reader.rs @@ -7,7 +7,7 @@ use core::ops::Range; use core::ptr; use core::slice; -use crate::de::ValueVisitor; +use crate::de::UnsizedVisitor; use crate::Context; /// Trait governing how a source of bytes is read. @@ -49,7 +49,7 @@ pub trait Reader<'de> { { struct Visitor<'a>(&'a mut [u8]); - impl<'a, 'de, C> ValueVisitor<'de, C, [u8]> for Visitor<'a> + impl<'a, 'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor<'a> where C: ?Sized + Context, { @@ -79,7 +79,7 @@ pub trait Reader<'de> { fn read_bytes(&mut self, cx: &C, n: usize, visitor: V) -> Result where C: ?Sized + Context, - V: ValueVisitor<'de, C, [u8]>; + V: UnsizedVisitor<'de, C, [u8]>; /// Read a single byte. #[inline] @@ -99,7 +99,7 @@ pub trait Reader<'de> { { struct Visitor([u8; N]); - impl<'de, const N: usize, C> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, const N: usize, C> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, { @@ -184,7 +184,7 @@ impl<'de> Reader<'de> for &'de [u8] { fn read_bytes(&mut self, cx: &C, n: usize, visitor: V) -> Result where C: ?Sized + Context, - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { if self.len() < n { return Err(cx.message("Buffer underflow")); @@ -325,7 +325,7 @@ impl<'de> Reader<'de> for SliceReader<'de> { fn read_bytes(&mut self, cx: &C, n: usize, visitor: V) -> Result where C: ?Sized + Context, - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { let outcome = bounds_check_add(cx, &self.range, n)?; @@ -443,7 +443,7 @@ where fn read_bytes(&mut self, cx: &C, n: usize, visitor: V) -> Result where C: ?Sized + Context, - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { self.bounds_check(cx, n)?; self.reader.read_bytes(cx, n, visitor) @@ -510,7 +510,7 @@ where fn read_bytes(&mut self, cx: &C, n: usize, visitor: V) -> Result where C: ?Sized + Context, - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { (**self).read_bytes(cx, n, visitor) } diff --git a/crates/musli/src/serde/deserializer.rs b/crates/musli/src/serde/deserializer.rs index 1f61915f8..812bcb181 100644 --- a/crates/musli/src/serde/deserializer.rs +++ b/crates/musli/src/serde/deserializer.rs @@ -218,7 +218,7 @@ where where V: de::Visitor<'de>, { - self.decoder.decode_unit()?; + self.decoder.decode_empty()?; visitor.visit_unit() } @@ -231,7 +231,7 @@ where where V: de::Visitor<'de>, { - self.decoder.decode_unit()?; + self.decoder.decode_empty()?; visitor.visit_unit() } @@ -447,7 +447,7 @@ impl BytesVisitor { } } -impl<'de, C, V> crate::de::ValueVisitor<'de, C, [u8]> for BytesVisitor +impl<'de, C, V> crate::de::UnsizedVisitor<'de, C, [u8]> for BytesVisitor where C: ?Sized + Context, C::Error: de::Error, @@ -516,10 +516,7 @@ where #[inline] fn size_hint(&self) -> Option { - match self.decoder.size_hint() { - SizeHint::Exact(n) => Some(n), - _ => None, - } + self.decoder.size_hint().into_option() } } @@ -581,7 +578,7 @@ impl StringVisitor { } } -impl<'de, C, V> crate::de::ValueVisitor<'de, C, str> for StringVisitor +impl<'de, C, V> crate::de::UnsizedVisitor<'de, C, str> for StringVisitor where C: ?Sized + Context, C::Error: de::Error, @@ -611,125 +608,6 @@ where } } -struct AnyNumberVisitor { - visitor: V, -} - -impl AnyNumberVisitor { - fn new(visitor: V) -> Self { - Self { visitor } - } -} - -impl<'de, C, V> crate::de::NumberVisitor<'de, C> for AnyNumberVisitor -where - C: ?Sized + Context, - C::Error: de::Error, - V: de::Visitor<'de>, -{ - type Ok = V::Value; - - #[inline] - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.visitor.expecting(f) - } - - #[inline] - fn visit_u8(self, _: &C, v: u8) -> Result { - self.visitor.visit_u8(v) - } - - #[inline] - fn visit_u16(self, _: &C, v: u16) -> Result { - self.visitor.visit_u16(v) - } - - #[inline] - fn visit_u32(self, _: &C, v: u32) -> Result { - self.visitor.visit_u32(v) - } - - #[inline] - fn visit_u64(self, _: &C, v: u64) -> Result { - self.visitor.visit_u64(v) - } - - #[inline] - fn visit_u128(self, _: &C, v: u128) -> Result { - // Serde's 128-bit support is very broken, so just try to avoid it if we can. - // See: https://github.com/serde-rs/serde/issues/2576 - if let Ok(v) = u64::try_from(v) { - return self.visitor.visit_u64(v); - } - - self.visitor.visit_u128(v) - } - - #[inline] - fn visit_i8(self, _: &C, v: i8) -> Result { - self.visitor.visit_i8(v) - } - - #[inline] - fn visit_i16(self, _: &C, v: i16) -> Result { - self.visitor.visit_i16(v) - } - - #[inline] - fn visit_i32(self, _: &C, v: i32) -> Result { - self.visitor.visit_i32(v) - } - - #[inline] - fn visit_i64(self, _: &C, v: i64) -> Result { - self.visitor.visit_i64(v) - } - - #[inline] - fn visit_i128(self, _: &C, v: i128) -> Result { - // Serde's 128-bit support is very broken, so just try to avoid it if we can. - // See: https://github.com/serde-rs/serde/issues/2576 - if let Ok(v) = i64::try_from(v) { - return self.visitor.visit_i64(v); - } - - self.visitor.visit_i128(v) - } - - #[inline] - fn visit_f32(self, _: &C, v: f32) -> Result { - self.visitor.visit_f32(v) - } - - #[inline] - fn visit_f64(self, _: &C, v: f64) -> Result { - self.visitor.visit_f64(v) - } - - #[inline] - fn visit_usize(self, cx: &C, v: usize) -> Result { - if let Some(value) = unsigned_value(self.visitor, v)? { - return Ok(value); - } - - Err(cx.message(format_args!("Unsupported usize value {v}"))) - } - - #[inline] - fn visit_isize(self, cx: &C, v: isize) -> Result { - if let Some(value) = signed_value(self.visitor, v)? { - return Ok(value); - } - - Err(cx.message(format_args!("Unsupported isize value {v}"))) - } - - #[inline] - fn visit_bytes(self, _: &C, v: &'de [u8]) -> Result { - self.visitor.visit_bytes(v) - } -} - struct EnumAccess<'de, 'a, D> where D: VariantDecoder<'de>, @@ -756,7 +634,7 @@ where #[inline] fn unit_variant(self) -> Result<(), Self::Error> { - self.decoder.decode_value()?.decode_unit() + self.decoder.decode_value()?.decode_empty() } #[inline] @@ -838,7 +716,6 @@ where type String = StringVisitor; type Bytes = BytesVisitor; - type Number = AnyNumberVisitor; #[inline] fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -846,7 +723,7 @@ where } #[inline] - fn visit_unit(self, _: &C) -> Result { + fn visit_empty(self, _: &C) -> Result { self.visitor.visit_unit() } @@ -991,11 +868,6 @@ where fn visit_bytes(self, _: &C, _: SizeHint) -> Result { Ok(BytesVisitor::new(self.visitor)) } - - #[inline] - fn visit_number(self, _: &C) -> Result { - Ok(AnyNumberVisitor::new(self.visitor)) - } } fn unsigned_value<'de, V, E>(visitor: V, v: usize) -> Result, E> diff --git a/crates/musli/src/serde/serializer.rs b/crates/musli/src/serde/serializer.rs index 46f877502..b6f47573b 100644 --- a/crates/musli/src/serde/serializer.rs +++ b/crates/musli/src/serde/serializer.rs @@ -136,12 +136,12 @@ where #[inline] fn serialize_unit(self) -> Result { - self.encoder.encode_unit() + self.encoder.encode_empty() } #[inline] fn serialize_unit_struct(self, _: &'static str) -> Result { - self.encoder.encode_unit() + self.encoder.encode_empty() } #[inline] @@ -152,7 +152,7 @@ where variant_name: &'static str, ) -> Result { encode_variant(self.cx, self.encoder, variant_name, |encoder| { - encoder.encode_unit() + encoder.encode_empty() }) } diff --git a/crates/musli/src/storage/de.rs b/crates/musli/src/storage/de.rs index 1b0717c57..ddec7f613 100644 --- a/crates/musli/src/storage/de.rs +++ b/crates/musli/src/storage/de.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use crate::de::{ DecodeUnsized, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, SequenceDecoder, SizeHint, - ValueVisitor, VariantDecoder, + UnsizedVisitor, VariantDecoder, }; use crate::hint::{MapHint, SequenceHint}; use crate::{Context, Decode, Options, Reader}; @@ -89,7 +89,7 @@ where } #[inline] - fn decode_unit(mut self) -> Result<(), C::Error> { + fn decode_empty(mut self) -> Result<(), C::Error> { let mark = self.cx.mark(); let count = crate::int::decode_usize::<_, _, OPT>(self.cx, self.reader.borrow_mut())?; @@ -118,7 +118,7 @@ where #[inline] fn decode_bytes(mut self, visitor: V) -> Result where - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { let len = crate::int::decode_usize::<_, _, OPT>(self.cx, self.reader.borrow_mut())?; self.reader.read_bytes(self.cx, len, visitor) @@ -127,14 +127,14 @@ where #[inline] fn decode_string(self, visitor: V) -> Result where - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { struct Visitor(V); - impl<'de, C, V> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C, V> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { type Ok = V::Ok; @@ -390,7 +390,7 @@ where #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Exact(self.remaining) + SizeHint::exact(self.remaining) } #[inline] @@ -428,7 +428,7 @@ where #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Exact(self.remaining) + SizeHint::exact(self.remaining) } #[inline] diff --git a/crates/musli/src/storage/en.rs b/crates/musli/src/storage/en.rs index 561878176..6b81acea9 100644 --- a/crates/musli/src/storage/en.rs +++ b/crates/musli/src/storage/en.rs @@ -67,7 +67,7 @@ where } #[inline] - fn encode_unit(self) -> Result { + fn encode_empty(self) -> Result { static HINT: SequenceHint = SequenceHint::with_size(0); self.encode_sequence_fn(&HINT, |_| Ok(())) } diff --git a/crates/musli/src/storage/mod.rs b/crates/musli/src/storage/mod.rs index f3e6f80db..60f14329f 100644 --- a/crates/musli/src/storage/mod.rs +++ b/crates/musli/src/storage/mod.rs @@ -116,6 +116,6 @@ pub use self::encoding::to_vec; #[doc(inline)] pub use self::encoding::to_writer; #[doc(inline)] -pub use self::encoding::{decode, encode, from_slice, to_fixed_bytes, Encoding, OPTIONS}; +pub use self::encoding::{decode, encode, from_slice, to_fixed_bytes, Encoding, DEFAULT, OPTIONS}; #[doc(inline)] pub use self::error::Error; diff --git a/crates/musli/src/system/no_std.rs b/crates/musli/src/system/no_std.rs deleted file mode 100644 index 073c11a1f..000000000 --- a/crates/musli/src/system/no_std.rs +++ /dev/null @@ -1,11 +0,0 @@ -extern "C" { - /// Abort the program in no-std environments. - /// - /// This has to be implemented by the caller, and is used for unrecoverable - /// and unusual errors. Such as when a reference count is overflowing. - fn __musli_abort() -> !; -} - -pub(crate) fn abort() -> ! { - unsafe { __musli_abort() } -} diff --git a/crates/musli/src/system/std.rs b/crates/musli/src/system/std.rs deleted file mode 100644 index f6c675357..000000000 --- a/crates/musli/src/system/std.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) use ::std::process::abort; diff --git a/crates/musli/src/value/de.rs b/crates/musli/src/value/de.rs index d0aa71a66..e129146a2 100644 --- a/crates/musli/src/value/de.rs +++ b/crates/musli/src/value/de.rs @@ -2,7 +2,7 @@ use core::fmt; use core::slice; #[cfg(feature = "alloc")] -use crate::de::ValueVisitor; +use crate::de::UnsizedVisitor; use crate::de::{ AsDecoder, Decode, DecodeUnsized, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, SequenceDecoder, SizeHint, Skip, VariantDecoder, Visitor, @@ -148,7 +148,7 @@ impl<'a, 'de, C: ?Sized + Context, const OPT: Options> Decoder<'de> } #[inline] - fn decode_unit(self) -> Result<(), C::Error> { + fn decode_empty(self) -> Result<(), C::Error> { ensure!(self, hint, ExpectedUnit(hint), Value::Unit => Ok(())) } @@ -244,7 +244,7 @@ impl<'a, 'de, C: ?Sized + Context, const OPT: Options> Decoder<'de> #[inline] fn decode_bytes(self, visitor: V) -> Result where - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { ensure!(self, hint, ExpectedBytes(hint), Value::Bytes(bytes) => { visitor.visit_borrowed(self.cx, bytes) @@ -255,7 +255,7 @@ impl<'a, 'de, C: ?Sized + Context, const OPT: Options> Decoder<'de> #[inline] fn decode_string(self, visitor: V) -> Result where - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { ensure!(self, hint, ExpectedString(hint), Value::String(string) => { visitor.visit_borrowed(self.cx, string) @@ -350,7 +350,7 @@ impl<'a, 'de, C: ?Sized + Context, const OPT: Options> Decoder<'de> V: Visitor<'de, Self::Cx>, { match self.value { - Value::Unit => visitor.visit_unit(self.cx), + Value::Unit => visitor.visit_empty(self.cx), Value::Bool(value) => visitor.visit_bool(self.cx, *value), Value::Char(value) => visitor.visit_char(self.cx, *value), Value::Number(number) => match number { @@ -371,12 +371,12 @@ impl<'a, 'de, C: ?Sized + Context, const OPT: Options> Decoder<'de> }, #[cfg(feature = "alloc")] Value::Bytes(bytes) => { - let visitor = visitor.visit_bytes(self.cx, SizeHint::Exact(bytes.len()))?; + let visitor = visitor.visit_bytes(self.cx, SizeHint::exact(bytes.len()))?; visitor.visit_borrowed(self.cx, bytes) } #[cfg(feature = "alloc")] Value::String(string) => { - let visitor = visitor.visit_string(self.cx, SizeHint::Exact(string.len()))?; + let visitor = visitor.visit_string(self.cx, SizeHint::exact(string.len()))?; visitor.visit_borrowed(self.cx, string) } #[cfg(feature = "alloc")] diff --git a/crates/musli/src/value/en.rs b/crates/musli/src/value/en.rs index a81e70c59..633cea37c 100644 --- a/crates/musli/src/value/en.rs +++ b/crates/musli/src/value/en.rs @@ -124,7 +124,7 @@ where } #[inline] - fn encode_unit(self) -> Result { + fn encode_empty(self) -> Result { Ok(()) } @@ -327,7 +327,7 @@ where { let mut variant = self.encode_variant()?; variant.encode_tag()?.encode(tag)?; - variant.encode_data()?.encode_unit()?; + variant.encode_data()?.encode_empty()?; variant.finish_variant()?; Ok(()) } diff --git a/crates/musli/src/value/value.rs b/crates/musli/src/value/value.rs index 20fc2d17d..b995fa30d 100644 --- a/crates/musli/src/value/value.rs +++ b/crates/musli/src/value/value.rs @@ -10,8 +10,7 @@ use alloc::vec::Vec; use crate::de::{AsDecoder, Decode, Decoder, Visitor}; #[cfg(feature = "alloc")] use crate::de::{ - EntryDecoder, MapDecoder, NumberVisitor, SequenceDecoder, SizeHint, ValueVisitor, - VariantDecoder, + EntryDecoder, MapDecoder, SequenceDecoder, SizeHint, UnsizedVisitor, VariantDecoder, }; use crate::en::{Encode, Encoder}; #[cfg(feature = "alloc")] @@ -85,13 +84,13 @@ impl Value { Value::Char(..) => TypeHint::Char, Value::Number(number) => TypeHint::Number(number.type_hint()), #[cfg(feature = "alloc")] - Value::Bytes(bytes) => TypeHint::Bytes(SizeHint::Exact(bytes.len())), + Value::Bytes(bytes) => TypeHint::Bytes(SizeHint::exact(bytes.len())), #[cfg(feature = "alloc")] - Value::String(string) => TypeHint::String(SizeHint::Exact(string.len())), + Value::String(string) => TypeHint::String(SizeHint::exact(string.len())), #[cfg(feature = "alloc")] - Value::Sequence(sequence) => TypeHint::Sequence(SizeHint::Exact(sequence.len())), + Value::Sequence(sequence) => TypeHint::Sequence(SizeHint::exact(sequence.len())), #[cfg(feature = "alloc")] - Value::Map(map) => TypeHint::Map(SizeHint::Exact(map.len())), + Value::Map(map) => TypeHint::Map(SizeHint::exact(map.len())), #[cfg(feature = "alloc")] Value::Variant(..) => TypeHint::Variant, #[cfg(feature = "alloc")] @@ -213,8 +212,6 @@ impl<'de, C: ?Sized + Context> Visitor<'de, C> for AnyVisitor { type String = StringVisitor; #[cfg(feature = "alloc")] type Bytes = BytesVisitor; - #[cfg(feature = "alloc")] - type Number = ValueNumberVisitor; #[inline] fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -222,7 +219,7 @@ impl<'de, C: ?Sized + Context> Visitor<'de, C> for AnyVisitor { } #[inline] - fn visit_unit(self, _: &C) -> Result { + fn visit_empty(self, _: &C) -> Result { Ok(Value::Unit) } @@ -362,12 +359,6 @@ impl<'de, C: ?Sized + Context> Visitor<'de, C> for AnyVisitor { Ok(StringVisitor) } - #[cfg(feature = "alloc")] - #[inline] - fn visit_number(self, _: &C) -> Result { - Ok(ValueNumberVisitor) - } - #[cfg(feature = "alloc")] #[inline] fn visit_variant(self, _: &C, variant: &mut D) -> Result @@ -394,7 +385,7 @@ impl<'de, M> Decode<'de, M> for Value { struct BytesVisitor; #[cfg(feature = "alloc")] -impl<'de, C: ?Sized + Context> ValueVisitor<'de, C, [u8]> for BytesVisitor { +impl<'de, C: ?Sized + Context> UnsizedVisitor<'de, C, [u8]> for BytesVisitor { type Ok = Value; #[inline] @@ -418,7 +409,7 @@ impl<'de, C: ?Sized + Context> ValueVisitor<'de, C, [u8]> for BytesVisitor { struct StringVisitor; #[cfg(feature = "alloc")] -impl<'de, C: ?Sized + Context> ValueVisitor<'de, C, str> for StringVisitor { +impl<'de, C: ?Sized + Context> UnsizedVisitor<'de, C, str> for StringVisitor { type Ok = Value; #[inline] @@ -437,96 +428,13 @@ impl<'de, C: ?Sized + Context> ValueVisitor<'de, C, str> for StringVisitor { } } -#[cfg(feature = "alloc")] -struct ValueNumberVisitor; - -#[cfg(feature = "alloc")] -impl<'de, C: ?Sized + Context> NumberVisitor<'de, C> for ValueNumberVisitor { - type Ok = Value; - - #[inline] - fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "any supported number") - } - - #[inline] - fn visit_u8(self, _: &C, value: u8) -> Result { - Ok(Value::Number(Number::U8(value))) - } - - #[inline] - fn visit_u16(self, _: &C, value: u16) -> Result { - Ok(Value::Number(Number::U16(value))) - } - - #[inline] - fn visit_u32(self, _: &C, value: u32) -> Result { - Ok(Value::Number(Number::U32(value))) - } - - #[inline] - fn visit_u64(self, _: &C, value: u64) -> Result { - Ok(Value::Number(Number::U64(value))) - } - - #[inline] - fn visit_u128(self, _: &C, value: u128) -> Result { - Ok(Value::Number(Number::U128(value))) - } - - #[inline] - fn visit_i8(self, _: &C, value: i8) -> Result { - Ok(Value::Number(Number::I8(value))) - } - - #[inline] - fn visit_i16(self, _: &C, value: i16) -> Result { - Ok(Value::Number(Number::I16(value))) - } - - #[inline] - fn visit_i32(self, _: &C, value: i32) -> Result { - Ok(Value::Number(Number::I32(value))) - } - - #[inline] - fn visit_i64(self, _: &C, value: i64) -> Result { - Ok(Value::Number(Number::I64(value))) - } - - #[inline] - fn visit_i128(self, _: &C, value: i128) -> Result { - Ok(Value::Number(Number::I128(value))) - } - - #[inline] - fn visit_f32(self, _: &C, value: f32) -> Result { - Ok(Value::Number(Number::F32(value))) - } - - #[inline] - fn visit_f64(self, _: &C, value: f64) -> Result { - Ok(Value::Number(Number::F64(value))) - } - - #[inline] - fn visit_usize(self, _: &C, value: usize) -> Result { - Ok(Value::Number(Number::Usize(value))) - } - - #[inline] - fn visit_isize(self, _: &C, value: isize) -> Result { - Ok(Value::Number(Number::Isize(value))) - } -} - impl Encode for Value { fn encode(&self, _: &E::Cx, encoder: E) -> Result where E: Encoder, { match self { - Value::Unit => encoder.encode_unit(), + Value::Unit => encoder.encode_empty(), Value::Bool(b) => encoder.encode_bool(*b), Value::Char(c) => encoder.encode_char(*c), Value::Number(n) => encoder.encode(n), diff --git a/crates/musli/src/wire/de.rs b/crates/musli/src/wire/de.rs index 6f56d4a88..ee4db49c9 100644 --- a/crates/musli/src/wire/de.rs +++ b/crates/musli/src/wire/de.rs @@ -6,7 +6,7 @@ use alloc::vec::Vec; use crate::de::{ Decode, DecodeUnsized, Decoder, EntriesDecoder, EntryDecoder, MapDecoder, SequenceDecoder, - SizeHint, Skip, ValueVisitor, VariantDecoder, + SizeHint, Skip, UnsizedVisitor, VariantDecoder, }; use crate::hint::{MapHint, SequenceHint}; use crate::int::continuation as c; @@ -268,7 +268,7 @@ where } #[inline] - fn decode_unit(self) -> Result<(), C::Error> { + fn decode_empty(self) -> Result<(), C::Error> { self.skip() } @@ -306,7 +306,7 @@ where #[inline] fn decode_bytes(mut self, visitor: V) -> Result where - V: ValueVisitor<'de, C, [u8]>, + V: UnsizedVisitor<'de, C, [u8]>, { let mark = self.cx.mark(); let len = self.decode_len(mark)?; @@ -316,14 +316,14 @@ where #[inline] fn decode_string(self, visitor: V) -> Result where - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { struct Visitor(V); - impl<'de, C, V> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C, V> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, - V: ValueVisitor<'de, C, str>, + V: UnsizedVisitor<'de, C, str>, { type Ok = V::Ok; @@ -561,7 +561,7 @@ where #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Exact(self.remaining) + SizeHint::exact(self.remaining) } #[inline] @@ -620,7 +620,7 @@ where #[inline] fn size_hint(&self) -> SizeHint { - SizeHint::Exact(self.remaining) + SizeHint::exact(self.remaining) } #[inline] diff --git a/crates/musli/src/wire/en.rs b/crates/musli/src/wire/en.rs index 5024fb050..bb084cd31 100644 --- a/crates/musli/src/wire/en.rs +++ b/crates/musli/src/wire/en.rs @@ -122,7 +122,7 @@ where } #[inline] - fn encode_unit(mut self) -> Result { + fn encode_empty(mut self) -> Result { self.writer .write_byte(self.cx, Tag::new(Kind::Sequence, 0).byte())?; Ok(()) diff --git a/crates/musli/src/wire/mod.rs b/crates/musli/src/wire/mod.rs index 54c719457..8b613024d 100644 --- a/crates/musli/src/wire/mod.rs +++ b/crates/musli/src/wire/mod.rs @@ -114,7 +114,7 @@ pub use self::encoding::to_vec; #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub use self::encoding::to_writer; #[doc(inline)] -pub use self::encoding::{decode, encode, from_slice, to_fixed_bytes, Encoding, OPTIONS}; +pub use self::encoding::{decode, encode, from_slice, to_fixed_bytes, Encoding, DEFAULT, OPTIONS}; #[doc(inline)] pub use self::error::Error; diff --git a/crates/musli/src/wire/tag.rs b/crates/musli/src/wire/tag.rs index 0cd409836..4bf0c2ebf 100644 --- a/crates/musli/src/wire/tag.rs +++ b/crates/musli/src/wire/tag.rs @@ -50,10 +50,12 @@ pub(crate) struct Tag { impl Tag { /// Construct a new tag through an unchecked constructor. /// - /// `data` must not be equal to or larger than [MAX_INLINE_LEN], or else it - /// could corrupt the payload. + /// The `data` argument must fit within the numerical bounds specified by + /// [`DATA_MASK`]. #[inline] pub(crate) const fn new(kind: Kind, data: u8) -> Self { + debug_assert!(data <= DATA_MASK, "Data must fit in DATA_MASK"); + Self { repr: kind as u8 | data, } diff --git a/crates/musli/tests/visitors.rs b/crates/musli/tests/visitors.rs index a377fa37d..dbd65b2dc 100644 --- a/crates/musli/tests/visitors.rs +++ b/crates/musli/tests/visitors.rs @@ -1,6 +1,6 @@ use std::fmt; -use musli::de::ValueVisitor; +use musli::de::UnsizedVisitor; use musli::{Context, Decode, Decoder}; #[derive(Debug, PartialEq)] @@ -16,7 +16,7 @@ impl<'de, M> Decode<'de, M> for BytesReference<'de> { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor where C: ?Sized + Context, { @@ -73,7 +73,7 @@ impl<'de, M> Decode<'de, M> for StringReference<'de> { { struct Visitor; - impl<'de, C> ValueVisitor<'de, C, str> for Visitor + impl<'de, C> UnsizedVisitor<'de, C, str> for Visitor where C: ?Sized + Context, { diff --git a/no-std/examples/json.rs b/no-std/examples/json.rs index 992f0f367..e82a59ff7 100644 --- a/no-std/examples/json.rs +++ b/no-std/examples/json.rs @@ -32,12 +32,6 @@ extern "C" fn eh_personality() {} #[no_mangle] pub extern "C" fn _Unwind_Resume() {} -// This needs to be implemented since core::intrinsics::abort is not stable. -#[no_mangle] -extern "C" fn __musli_abort() -> ! { - core::intrinsics::abort(); -} - #[derive(Debug, Encode, Decode)] struct Value<'a> { name: &'a str,