diff --git a/benchmark/benchmark.rs b/benchmark/benchmark.rs index e95316b6e..57591fbc2 100644 --- a/benchmark/benchmark.rs +++ b/benchmark/benchmark.rs @@ -470,7 +470,7 @@ fn try_main() -> ::capnp::Result<()> { return Err(::capnp::Error::failed(format!( "Could not parse a u64 from: {}", args[5] - ))) + ))); }; let mode = Mode::parse(&args[2])?; diff --git a/capnp-futures/src/serialize.rs b/capnp-futures/src/serialize.rs index 42a263773..6d471f256 100644 --- a/capnp-futures/src/serialize.rs +++ b/capnp-futures/src/serialize.rs @@ -52,7 +52,7 @@ where R: AsyncRead + Unpin, { let Some(segment_lengths_builder) = read_segment_table(&mut reader, options).await? else { - return Ok(None) + return Ok(None); }; Ok(Some( read_segments( diff --git a/capnp-rpc/src/rpc.rs b/capnp-rpc/src/rpc.rs index 6fedfa375..fb3363493 100644 --- a/capnp-rpc/src/rpc.rs +++ b/capnp-rpc/src/rpc.rs @@ -355,12 +355,16 @@ fn to_pipeline_ops( } fn from_error(error: &Error, mut builder: exception::Builder) { - builder.set_reason(&error.description); + builder.set_reason(&error.to_string()); let typ = match error.kind { ::capnp::ErrorKind::Failed => exception::Type::Failed, ::capnp::ErrorKind::Overloaded => exception::Type::Overloaded, ::capnp::ErrorKind::Disconnected => exception::Type::Disconnected, ::capnp::ErrorKind::Unimplemented => exception::Type::Unimplemented, + ::capnp::ErrorKind::SettingDynamicCapabilitiesIsUnsupported => { + exception::Type::Unimplemented + } + _ => exception::Type::Failed, }; builder.set_type(typ); } @@ -378,7 +382,7 @@ fn remote_exception_to_error(exception: exception::Reader) -> Error { _ => (::capnp::ErrorKind::Failed, "(malformed error)"), }; Error { - description: format!("remote exception: {reason}"), + extra: format!("remote exception: {reason}"), kind, } } @@ -647,7 +651,7 @@ impl ConnectionState { let Some(state) = weak_state.upgrade() else { return Promise::err(Error::disconnected( "message loop cannot continue without a connection".into(), - )) + )); }; let promise = match *state.connection.borrow_mut() { @@ -894,7 +898,7 @@ impl ConnectionState { let Some(connection_state) = weak_state.upgrade() else { return Err(Error::disconnected( "handle_message() cannot continue without a connection".into(), - )) + )); }; let reader = message.get_body()?.get_as::()?; @@ -2041,7 +2045,7 @@ impl Pipeline { let resolve_self_promise = connection_state.eagerly_evaluate(fork.clone().then(move |response| { let Some(state) = this.upgrade() else { - return Promise::err(Error::failed("dangling reference to this".into())) + return Promise::err(Error::failed("dangling reference to this".into())); }; PipelineState::resolve(&state, response); Promise::ok(()) diff --git a/capnp-rpc/test/reconnect_test.rs b/capnp-rpc/test/reconnect_test.rs index e491bee16..c327b7a2b 100644 --- a/capnp-rpc/test/reconnect_test.rs +++ b/capnp-rpc/test/reconnect_test.rs @@ -101,8 +101,8 @@ macro_rules! assert_err { let e1 = $e1; let e2 = $e2; assert_eq!(e1.kind, e2.kind); - if !e1.description.ends_with(&e2.description) { - assert_eq!(e1.description, e2.description); + if !e1.extra.ends_with(&e2.extra) { + assert_eq!(e1.extra, e2.extra); } }; } diff --git a/capnp-rpc/test/test.rs b/capnp-rpc/test/test.rs index d83e1821c..f0d6268bd 100644 --- a/capnp-rpc/test/test.rs +++ b/capnp-rpc/test/test.rs @@ -335,9 +335,7 @@ fn pipelining_return_null() { let cap = request.send().pipeline.get_cap(); match cap.foo_request().send().promise.await { Err(ref e) => { - if e.description - .contains("Message contains null capability pointer") - { + if e.extra.contains("Message contains null capability pointer") { Ok(()) } else { Err(Error::failed(format!( diff --git a/capnp/Cargo.toml b/capnp/Cargo.toml index a9024f376..52275d081 100644 --- a/capnp/Cargo.toml +++ b/capnp/Cargo.toml @@ -21,7 +21,8 @@ quickcheck = { version = "1", optional = true } quickcheck = "1" [features] -default = ["std"] +alloc = [] +default = ["std", "alloc"] rpc_try = [] diff --git a/capnp/src/any_pointer.rs b/capnp/src/any_pointer.rs index c75228246..d93462d64 100644 --- a/capnp/src/any_pointer.rs +++ b/capnp/src/any_pointer.rs @@ -21,10 +21,12 @@ //! Untyped pointer that can be cast to any struct, list, or capability type. -use alloc::boxed::Box; -use alloc::vec::Vec; +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec::Vec}; +#[cfg(feature = "alloc")] use crate::capability::FromClientHook; +#[cfg(feature = "alloc")] use crate::private::capability::{ClientHook, PipelineHook, PipelineOp}; use crate::private::layout::{PointerBuilder, PointerReader}; use crate::traits::{FromPointerBuilder, FromPointerReader, SetPointerBuilder}; @@ -44,6 +46,7 @@ impl crate::introspect::Introspect for Owned { } } +#[cfg(feature = "alloc")] impl crate::traits::Pipelined for Owned { type Pipeline = Pipeline; } @@ -73,12 +76,14 @@ impl<'a> Reader<'a> { FromPointerReader::get_from_pointer(&self.reader, None) } + #[cfg(feature = "alloc")] pub fn get_as_capability(&self) -> Result { Ok(FromClientHook::new(self.reader.get_capability()?)) } //# Used by RPC system to implement pipelining. Applications //# generally shouldn't use this directly. + #[cfg(feature = "alloc")] pub fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Result> { let mut pointer = self.reader; @@ -117,6 +122,7 @@ impl<'a> crate::traits::SetPointerBuilder for Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> crate::traits::Imbue<'a> for Reader<'a> { fn imbue(&mut self, cap_table: &'a crate::private::layout::CapTable) { self.reader @@ -165,6 +171,7 @@ impl<'a> Builder<'a> { } // XXX value should be a user client. + #[cfg(feature = "alloc")] pub fn set_as_capability(&mut self, value: Box) { self.builder.set_capability(value); } @@ -199,6 +206,7 @@ impl<'a> FromPointerBuilder<'a> for Builder<'a> { } } +#[cfg(feature = "alloc")] impl<'a> crate::traits::ImbueMut<'a> for Builder<'a> { fn imbue_mut(&mut self, cap_table: &'a mut crate::private::layout::CapTable) { self.builder @@ -206,6 +214,7 @@ impl<'a> crate::traits::ImbueMut<'a> for Builder<'a> { } } +#[cfg(feature = "alloc")] pub struct Pipeline { // XXX this should not be public pub hook: Box, @@ -213,6 +222,7 @@ pub struct Pipeline { ops: Vec, } +#[cfg(feature = "alloc")] impl Pipeline { pub fn new(hook: Box) -> Self { Self { @@ -245,24 +255,28 @@ impl Pipeline { } } +#[cfg(feature = "alloc")] impl crate::capability::FromTypelessPipeline for Pipeline { fn new(typeless: Pipeline) -> Self { typeless } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Reader<'a> { fn from(a: Reader<'a>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::AnyPointer(a) } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Builder<'a> { fn from(a: Builder<'a>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::AnyPointer(a) } } +#[cfg(feature = "alloc")] #[test] fn init_clears_value() { let mut message = crate::message::Builder::new_default(); diff --git a/capnp/src/any_pointer_list.rs b/capnp/src/any_pointer_list.rs index 1c4e9e40c..9c2c63adf 100644 --- a/capnp/src/any_pointer_list.rs +++ b/capnp/src/any_pointer_list.rs @@ -189,6 +189,7 @@ impl<'a> core::iter::IntoIterator for Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Reader<'a> { fn from(t: Reader<'a>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::List(crate::dynamic_list::Reader::new( @@ -198,6 +199,7 @@ impl<'a> From> for crate::dynamic_value::Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Builder<'a> { fn from(t: Builder<'a>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::List(crate::dynamic_list::Builder::new( diff --git a/capnp/src/capability.rs b/capnp/src/capability.rs index 9d72aafe5..7c6d2b072 100644 --- a/capnp/src/capability.rs +++ b/capnp/src/capability.rs @@ -22,6 +22,7 @@ //! Hooks for for the RPC system. //! //! Roughly corresponds to capability.h in the C++ implementation. +#![cfg(feature = "alloc")] use alloc::boxed::Box; use core::future::Future; diff --git a/capnp/src/capability_list.rs b/capnp/src/capability_list.rs index 740d21ebb..bca9d2380 100644 --- a/capnp/src/capability_list.rs +++ b/capnp/src/capability_list.rs @@ -19,6 +19,7 @@ // THE SOFTWARE. //! List of capabilities. +#![cfg(feature = "alloc")] use alloc::boxed::Box; use core::marker::PhantomData; diff --git a/capnp/src/data.rs b/capnp/src/data.rs index 89e0c6c19..4e8c49d96 100644 --- a/capnp/src/data.rs +++ b/capnp/src/data.rs @@ -82,12 +82,14 @@ impl<'a> crate::traits::SetPointerBuilder for Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Reader<'a> { fn from(d: Reader<'a>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::Data(d) } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Builder<'a> { fn from(d: Builder<'a>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::Data(d) diff --git a/capnp/src/data_list.rs b/capnp/src/data_list.rs index f5fee2575..042e4e895 100644 --- a/capnp/src/data_list.rs +++ b/capnp/src/data_list.rs @@ -199,6 +199,7 @@ impl<'a> ::core::iter::IntoIterator for Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Reader<'a> { fn from(t: Reader<'a>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::List(crate::dynamic_list::Reader { @@ -208,6 +209,7 @@ impl<'a> From> for crate::dynamic_value::Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Builder<'a> { fn from(t: Builder<'a>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::List(crate::dynamic_list::Builder { diff --git a/capnp/src/dynamic_list.rs b/capnp/src/dynamic_list.rs index 58cffe914..bfc83ae25 100644 --- a/capnp/src/dynamic_list.rs +++ b/capnp/src/dynamic_list.rs @@ -1,10 +1,11 @@ //! Dynamically-typed lists. +#![cfg(feature = "alloc")] use crate::dynamic_value; use crate::introspect::{Type, TypeVariant}; use crate::private::layout::{self, PrimitiveElement}; use crate::traits::{IndexMove, ListIter}; -use crate::Result; +use crate::{Error, ErrorKind, Result}; /// A read-only dynamically-typed list. #[derive(Copy, Clone)] @@ -321,13 +322,13 @@ impl<'a> Builder<'a> { .reborrow() .get_pointer_element(index) .set_list(&list.reader, false), - (TypeVariant::AnyPointer, _) => Err(crate::Error::failed( - "List(AnyPointer) not supported".into(), - )), - (TypeVariant::Capability, dynamic_value::Reader::Capability(_)) => Err( - crate::Error::failed("List(Capability) not supported".into()), - ), - (_, _) => Err(crate::Error::failed("Type mismatch".into())), + (TypeVariant::AnyPointer, _) => { + Err(Error::from_kind(ErrorKind::ListAnyPointerNotSupported)) + } + (TypeVariant::Capability, dynamic_value::Reader::Capability(_)) => { + Err(Error::from_kind(ErrorKind::ListCapabilityNotSupported)) + } + (_, _) => Err(Error::from_kind(ErrorKind::TypeMismatch)), } } @@ -348,9 +349,7 @@ impl<'a> Builder<'a> { | TypeVariant::Float64 | TypeVariant::Enum(_) | TypeVariant::Struct(_) - | TypeVariant::Capability => { - Err(crate::Error::failed("Expected a list or blob.".into())) - } + | TypeVariant::Capability => Err(Error::from_kind(ErrorKind::ExpectedAListOrBlob)), TypeVariant::Text => Ok(self .builder .get_pointer_element(index) @@ -378,9 +377,7 @@ impl<'a> Builder<'a> { ) .into()), }, - TypeVariant::AnyPointer => Err(crate::Error::failed( - "List(AnyPointer) not supported.".into(), - )), + TypeVariant::AnyPointer => Err(Error::from_kind(ErrorKind::ListAnyPointerNotSupported)), } } } diff --git a/capnp/src/dynamic_struct.rs b/capnp/src/dynamic_struct.rs index dcf6552c5..fb8090509 100644 --- a/capnp/src/dynamic_struct.rs +++ b/capnp/src/dynamic_struct.rs @@ -1,11 +1,12 @@ //! Dynamically-typed structs. +#![cfg(feature = "alloc")] use crate::introspect::TypeVariant; use crate::private::layout; use crate::schema::{Field, StructSchema}; use crate::schema_capnp::{field, node, value}; -use crate::Result; use crate::{dynamic_list, dynamic_value}; +use crate::{Error, ErrorKind, Result}; fn has_discriminant_value(reader: field::Reader) -> bool { reader.get_discriminant_value() != field::NO_DISCRIMINANT @@ -18,7 +19,7 @@ pub(crate) fn struct_size_from_schema(schema: StructSchema) -> Result Reader<'a> { (TypeVariant::Capability, value::Interface(())) => { Ok(dynamic_value::Reader::Capability(dynamic_value::Capability)) } - _ => Err(crate::Error::failed("field and default mismatch".into())), + _ => Err(Error::from_kind(ErrorKind::FieldAndDefaultMismatch)), } } field::Group(_) => { if let TypeVariant::Struct(schema) = ty.which() { Ok(Reader::new(self.reader, schema.into()).into()) } else { - Err(crate::Error::failed( - "group field but type is not Struct".into(), - )) + Err(Error::from_kind(ErrorKind::GroupFieldButTypeIsNotStruct)) } } } @@ -184,7 +183,7 @@ impl<'a> Reader<'a> { /// Otherwise, returns None. pub fn which(&self) -> Result> { let node::Struct(st) = self.schema.get_proto().which()? else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; if st.get_discriminant_count() == 0 { Ok(None) @@ -202,7 +201,7 @@ impl<'a> Reader<'a> { let proto = field.get_proto(); if has_discriminant_value(proto) { let node::Struct(st) = self.schema.get_proto().which()? else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; let discrim = self @@ -392,16 +391,14 @@ impl<'a> Builder<'a> { (TypeVariant::Capability, value::Interface(())) => Ok( dynamic_value::Builder::Capability(dynamic_value::Capability), ), - _ => Err(crate::Error::failed("field and default mismatch".into())), + _ => Err(Error::from_kind(ErrorKind::FieldAndDefaultMismatch)), } } field::Group(_) => { if let TypeVariant::Struct(schema) = ty.which() { Ok(Builder::new(self.builder, schema.into()).into()) } else { - Err(crate::Error::failed( - "group field but type is not Struct".into(), - )) + Err(Error::from_kind(ErrorKind::GroupFieldButTypeIsNotStruct)) } } } @@ -414,7 +411,7 @@ impl<'a> Builder<'a> { pub fn which(&self) -> Result> { let node::Struct(st) = self.schema.get_proto().which()? else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; if st.get_discriminant_count() == 0 { Ok(None) @@ -518,28 +515,26 @@ impl<'a> Builder<'a> { dynamic_value::Reader::Data(t) => target.set_as(t), dynamic_value::Reader::Struct(s) => target.set_as(s), dynamic_value::Reader::List(l) => target.set_as(l), - dynamic_value::Reader::Capability(_) => { - Err(crate::Error::unimplemented( - "setting dynamic capabilities is unsupported".into(), - )) - } - _ => Err(crate::Error::failed( - "cannot set AnyPointer field to a primitive value".into(), + dynamic_value::Reader::Capability(_) => Err(Error::from_kind( + ErrorKind::SettingDynamicCapabilitiesIsUnsupported, + )), + _ => Err(Error::from_kind( + ErrorKind::CannotSetAnyPointerFieldToAPrimitiveValue, )), } } - (TypeVariant::Capability, _, _) => Err(crate::Error::unimplemented( - "setting dynamic capabilities is unsupported".into(), + (TypeVariant::Capability, _, _) => Err(Error::from_kind( + ErrorKind::SettingDynamicCapabilitiesIsUnsupported, )), - _ => Err(crate::Error::failed("type mismatch".into())), + _ => Err(Error::from_kind(ErrorKind::TypeMismatch)), } } field::Group(_group) => { let dynamic_value::Reader::Struct(src) = value else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; let dynamic_value::Builder::Struct(mut dst) = self.reborrow().init(field)? else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; if let Some(union_field) = src.which()? { dst.set(union_field, src.get(union_field)?)?; @@ -583,15 +578,15 @@ impl<'a> Builder<'a> { p.clear(); Ok(crate::any_pointer::Builder::new(p).into()) } - _ => Err(crate::Error::failed( - "init() is only valid for struct and AnyPointer fields".into(), + _ => Err(Error::from_kind( + ErrorKind::InitIsOnlyValidForStructAndAnyPointerFields, )), } } field::Group(_) => { self.clear(field)?; let TypeVariant::Struct(schema) = ty.which() else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; Ok((Builder::new(self.builder, schema.into())).into()) } @@ -638,13 +633,13 @@ impl<'a> Builder<'a> { .init_data(size) .into()), - _ => Err(crate::Error::failed( - "initn() is only valid for list, text, or data fields".into(), + _ => Err(Error::from_kind( + ErrorKind::InitnIsOnlyValidForListTextOrDataFields, )), } } - field::Group(_) => Err(crate::Error::failed( - "initn() is only valid for list, text, or data fields".into(), + field::Group(_) => Err(Error::from_kind( + ErrorKind::InitnIsOnlyValidForListTextOrDataFields, )), } } @@ -689,7 +684,7 @@ impl<'a> Builder<'a> { } field::Group(_) => { let TypeVariant::Struct(schema) = ty.which() else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; let mut group = Builder::new(self.builder.reborrow(), schema.into()); @@ -716,7 +711,7 @@ impl<'a> Builder<'a> { fn set_in_union(&mut self, field: Field) -> Result<()> { if has_discriminant_value(field.get_proto()) { let node::Struct(st) = self.schema.get_proto().which()? else { - return Err(crate::Error::failed("not a struct".into())) + return Err(Error::from_kind(ErrorKind::NotAStruct)); }; self.builder.set_data_field::( st.get_discriminant_offset() as usize, diff --git a/capnp/src/dynamic_value.rs b/capnp/src/dynamic_value.rs index d7df181ce..e8b0317c7 100644 --- a/capnp/src/dynamic_value.rs +++ b/capnp/src/dynamic_value.rs @@ -1,5 +1,6 @@ //! Dynamically typed values. +#![cfg(feature = "alloc")] use crate::introspect::{self, TypeVariant}; use crate::schema_capnp::value; use crate::Result; @@ -59,7 +60,7 @@ impl<'a> Reader<'a> { } (value::Interface(()), TypeVariant::Capability) => Ok(Capability.into()), (value::AnyPointer(a), TypeVariant::AnyPointer) => Ok(a.into()), - _ => Err(crate::Error::failed("type mismatch".into())), + _ => Err(crate::Error::from_kind(crate::ErrorKind::TypeMismatch)), } } @@ -103,7 +104,9 @@ pub trait DowncastReader<'a> { impl<'a> DowncastReader<'a> for () { fn downcast_reader(value: Reader<'a>) -> () { - let Reader::Void = value else { panic!("error downcasting to void") }; + let Reader::Void = value else { + panic!("error downcasting to void") + }; } } @@ -222,7 +225,9 @@ pub trait DowncastBuilder<'a> { impl<'a> DowncastBuilder<'a> for () { fn downcast_builder(value: Builder<'a>) -> () { - let Builder::Void = value else { panic!("error downcasting to void") }; + let Builder::Void = value else { + panic!("error downcasting to void") + }; } } diff --git a/capnp/src/enum_list.rs b/capnp/src/enum_list.rs index 8fa713029..1c8faeab2 100644 --- a/capnp/src/enum_list.rs +++ b/capnp/src/enum_list.rs @@ -214,6 +214,7 @@ impl<'a, T: TryFrom> ::core::iter::IntoIterator for Re } } +#[cfg(feature = "alloc")] impl<'a, T: TryFrom + crate::introspect::Introspect> From> for crate::dynamic_value::Reader<'a> { @@ -225,6 +226,7 @@ impl<'a, T: TryFrom + crate::introspect::Introspect> F } } +#[cfg(feature = "alloc")] impl<'a, T: TryFrom + crate::introspect::Introspect> From> for crate::dynamic_value::Builder<'a> { diff --git a/capnp/src/introspect.rs b/capnp/src/introspect.rs index b8b51e124..463b581bb 100644 --- a/capnp/src/introspect.rs +++ b/capnp/src/introspect.rs @@ -1,5 +1,6 @@ //! Traits and types to support run-time type introspection, i.e. reflection. +#[cfg(feature = "alloc")] use crate::private::layout::ElementSize; /// A type that supports reflection. All types that can appear in a Cap'n Proto message @@ -70,6 +71,7 @@ impl Type { /// If this type T appears as List(T), then what is the expected /// element size of the list? + #[cfg(feature = "alloc")] pub(crate) fn expected_element_size(&self) -> ElementSize { if self.list_count > 0 { ElementSize::Pointer diff --git a/capnp/src/io.rs b/capnp/src/io.rs index 6f4ab987c..1698d08ae 100644 --- a/capnp/src/io.rs +++ b/capnp/src/io.rs @@ -1,9 +1,7 @@ //! Custom I/O traits that roughly mirror `std::io::{Read, BufRead, Write}`. //! This extra layer of indirection enables support of no-std environments. -use alloc::string::ToString; - -use crate::Result; +use crate::{Error, ErrorKind, Result}; /// A rough approximation of std::io::Read. pub trait Read { @@ -26,9 +24,7 @@ pub trait Read { } } if !buf.is_empty() { - Err(crate::Error::failed( - "failed to fill the whole buffer".to_string(), - )) + Err(Error::from_kind(ErrorKind::FailedToFillTheWholeBuffer)) } else { Ok(()) } @@ -92,13 +88,14 @@ mod std_impls { #[cfg(not(feature = "std"))] mod no_std_impls { use crate::io::{BufRead, Read, Write}; - use crate::{Error, Result}; + use crate::{Error, ErrorKind, Result}; + #[cfg(feature = "alloc")] use alloc::string::ToString; impl<'a> Write for &'a mut [u8] { fn write_all(&mut self, buf: &[u8]) -> Result<()> { if buf.len() > self.len() { - return Err(Error::failed("buffer is not large enough".to_string())); + return Err(Error::from_kind(ErrorKind::BufferNotLargeEnough)); } let amt = buf.len(); let (a, b) = core::mem::take(self).split_at_mut(amt); @@ -108,6 +105,7 @@ mod no_std_impls { } } + #[cfg(feature = "alloc")] impl Write for alloc::vec::Vec { fn write_all(&mut self, buf: &[u8]) -> Result<()> { self.extend_from_slice(buf); diff --git a/capnp/src/lib.rs b/capnp/src/lib.rs index 3ca1360d2..d7d3acee3 100644 --- a/capnp/src/lib.rs +++ b/capnp/src/lib.rs @@ -29,11 +29,13 @@ #![cfg_attr(feature = "rpc_try", feature(try_trait_v2))] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] #[macro_use] extern crate alloc; /// Code generated from /// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp). +#[cfg(feature = "alloc")] pub mod schema_capnp; pub mod any_pointer; @@ -63,7 +65,9 @@ pub mod text; pub mod text_list; pub mod traits; +#[cfg(feature = "alloc")] use alloc::string::String; +#[cfg(feature = "alloc")] use alloc::vec::Vec; /// @@ -80,6 +84,7 @@ pub struct Word { /// /// Constructs a word with the given bytes. /// +#[allow(clippy::too_many_arguments)] pub const fn word(b0: u8, b1: u8, b2: u8, b3: u8, b4: u8, b5: u8, b6: u8, b7: u8) -> Word { Word { raw_content: [b0, b1, b2, b3, b4, b5, b6, b7], @@ -88,6 +93,7 @@ pub const fn word(b0: u8, b1: u8, b2: u8, b3: u8, b4: u8, b5: u8, b6: u8, b7: u8 impl Word { /// Allocates a vec of `length` words, all set to zero. + #[cfg(feature = "alloc")] pub fn allocate_zeroed_vec(length: usize) -> Vec { vec![word(0, 0, 0, 0, 0, 0, 0, 0); length] } @@ -134,7 +140,7 @@ impl core::ops::AddAssign for MessageSize { } /// An enum value or union discriminant that was not found among those defined in a schema. -#[derive(PartialEq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct NotInSchema(pub u16); impl ::core::fmt::Display for NotInSchema { @@ -168,15 +174,17 @@ pub struct Error { /// should read only this field in making its decision. pub kind: ErrorKind, - /// Human-readable failure description. - pub description: String, + /// Extra context about error + #[cfg(feature = "alloc")] + pub extra: String, } /// The general nature of an error. The purpose of this enum is not to describe the error itself, /// but rather to describe how the client might want to respond to the error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] pub enum ErrorKind { - /// Something went wrong. This is the usual error kind. It includes decoding errors. + /// Something went wrong Failed, /// The call failed because of a temporary lack of resources. This could be space resources @@ -194,30 +202,247 @@ pub enum ErrorKind { /// The requested method is not implemented. The caller may wish to revert to a fallback /// approach based on other methods. Unimplemented, + + /// Buffer is not large enough + BufferNotLargeEnough, + + /// Cannot create a canonical message with a capability + CannotCreateACanonicalMessageWithACapability, + + /// Cannot set AnyPointer field to a primitive value + CannotSetAnyPointerFieldToAPrimitiveValue, + + /// Don't know how to handle non-STRUCT inline composite. + CantHandleNonStructInlineComposite, + + /// Empty buffer + EmptyBuffer, + + /// Empty slice + EmptySlice, + + /// Enum value or union discriminant {} was not present in schema + EnumValueOrUnionDiscriminantNotPresent(NotInSchema), + + /// Called get_writable_{data|text}_pointer() but existing list pointer is not byte-sized. + ExistingListPointerIsNotByteSized, + + /// Existing list value is incompatible with expected type. + ExistingListValueIsIncompatibleWithExpectedType, + + /// Called get_writable_{data|text|list|struct_list}_pointer() but existing pointer is not a list. + ExistingPointerIsNotAList, + + /// Expected a list or blob. + ExpectedAListOrBlob, + + /// Expected a pointer list, but got a list of data-only structs + ExpectedAPointerListButGotAListOfDataOnlyStructs, + + /// Expected a primitive list, but got a list of pointer-only structs + ExpectedAPrimitiveListButGotAListOfPointerOnlyStructs, + + /// failed to fill the whole buffer + FailedToFillTheWholeBuffer, + + /// field and default mismatch + FieldAndDefaultMismatch, + + /// field not found + FieldNotFound, + + /// Found bit list where struct list was expected; upgrading boolean lists to struct lists is no longer supported + FoundBitListWhereStructListWasExpected, + + /// Found struct list where bit list was expected. + FoundStructListWhereBitListWasExpected, + + /// Cannot represent 4 byte length as `usize`. This may indicate that you are running on 8 or 16 bit platform or message is too large. + FourByteLengthTooBigForUSize, + + /// Cannot represent 4 byte segment length as usize. This may indicate that you are running on 8 or 16 bit platform or segment is too large + FourByteSegmentLengthTooBigForUSize, + + /// group field but type is not Struct + GroupFieldButTypeIsNotStruct, + + /// init() is only valid for struct and AnyPointer fields + InitIsOnlyValidForStructAndAnyPointerFields, + + /// initn() is only valid for list, text, or data fields + InitnIsOnlyValidForListTextOrDataFields, + + /// InlineComposite list with non-STRUCT elements not supported. + InlineCompositeListWithNonStructElementsNotSupported, + + /// InlineComposite list's elements overrun its word count. + InlineCompositeListsElementsOverrunItsWordCount, + + /// InlineComposite lists of non-STRUCT type are not supported. + InlineCompositeListsOfNonStructTypeAreNotSupported, + + /// Too many or too few segments {segment_count} + InvalidNumberOfSegments(usize), + + /// Invalid segment id {id} + InvalidSegmentId(u32), + + /// List(AnyPointer) not supported. + ListAnyPointerNotSupported, + + /// List(Capability) not supported + ListCapabilityNotSupported, + + /// Malformed double-far pointer. + MalformedDoubleFarPointer, + + /// Message contains invalid capability pointer. + MessageContainsInvalidCapabilityPointer, + + /// Message contains list pointer of non-bytes where data was expected. + MessageContainsListPointerOfNonBytesWhereDataWasExpected, + + /// Message contains list pointer of non-bytes where text was expected. + MessageContainsListPointerOfNonBytesWhereTextWasExpected, + + /// Message contains list with incompatible element type. + MessageContainsListWithIncompatibleElementType, + + /// Message contains non-capability pointer where capability pointer was expected. + MessageContainsNonCapabilityPointerWhereCapabilityPointerWasExpected, + + /// Message contains non-struct pointer where struct pointer was expected. + MessageContainsNonStructPointerWhereStructPointerWasExpected, + + /// Message contains non-list pointer where data was expected. + MessageContainsNonListPointerWhereDataWasExpected, + + /// Message contains non-list pointer where list pointer was expected + MessageContainsNonListPointerWhereListPointerWasExpected, + + /// Message contains non-list pointer where text was expected. + MessageContainsNonListPointerWhereTextWasExpected, + + /// Message contains null capability pointer. + MessageContainsNullCapabilityPointer, + + /// Message contains out-of-bounds pointer, + MessageContainsOutOfBoundsPointer, + + /// Message contains text that is not NUL-terminated + MessageContainsTextThatIsNotNULTerminated, + + /// Message ends prematurely. Header claimed {header} words, but message only has {body} words, + MessageEndsPrematurely(usize, usize), + + /// Message is too deeply nested. + MessageIsTooDeeplyNested, + + /// Message is too deeply-nested or contains cycles. + MessageIsTooDeeplyNestedOrContainsCycles, + + /// Message was not aligned by 8 bytes boundary. Either ensure that message is properly aligned or compile `capnp` crate with \"unaligned\" feature enabled. + MessageNotAlignedBy8BytesBoundary, + + /// Message's size cannot be represented in usize + MessageSizeOverflow, + + /// Message is too large + MessageTooLarge(usize), + + /// Nesting limit exceeded + NestingLimitExceeded, + + /// Not a struct + NotAStruct, + + /// Only one of the section pointers is pointing to ourself + OnlyOneOfTheSectionPointersIsPointingToOurself, + + /// Packed input did not end cleanly on a segment boundary. + PackedInputDidNotEndCleanlyOnASegmentBoundary, + + /// Premature end of file + PrematureEndOfFile, + + /// Premature end of packed input. + PrematureEndOfPackedInput, + + /// Read limit exceeded + ReadLimitExceeded, + + /// setting dynamic capabilities is unsupported + SettingDynamicCapabilitiesIsUnsupported, + + /// Struct reader had bitwidth other than 1 + StructReaderHadBitwidthOtherThan1, + + /// Text blob missing NUL terminator. + TextBlobMissingNULTerminator, + + /// Text contains non-utf8 data + TextContainsNonUtf8Data(core::str::Utf8Error), + + /// Tried to read from null arena + TriedToReadFromNullArena, + + /// type mismatch + TypeMismatch, + + /// Detected unaligned segment. You must either ensure all of your segments are 8-byte aligned, + /// or you must enable the "unaligned" feature in the capnp crate + UnalignedSegment, + + /// Unexpected far pointer + UnexepectedFarPointer, + + /// Unknown pointer type. + UnknownPointerType, } impl Error { + #[cfg(feature = "alloc")] + pub fn extra(&mut self, message: String) { + self.extra = message; + } + + #[cfg(feature = "alloc")] pub fn failed(description: String) -> Self { Self { - description, + extra: description, kind: ErrorKind::Failed, } } + + pub fn from_kind(kind: ErrorKind) -> Self { + #[cfg(not(feature = "alloc"))] + return Self { kind }; + #[cfg(feature = "alloc")] + return Self { + kind, + extra: String::new(), + }; + } + + #[cfg(feature = "alloc")] pub fn overloaded(description: String) -> Self { Self { - description, + extra: description, kind: ErrorKind::Overloaded, } } + #[cfg(feature = "alloc")] pub fn disconnected(description: String) -> Self { Self { - description, + extra: description, kind: ErrorKind::Disconnected, } } + + #[cfg(feature = "alloc")] pub fn unimplemented(description: String) -> Self { Self { - description, + extra: description, kind: ErrorKind::Unimplemented, } } @@ -236,19 +461,24 @@ impl core::convert::From<::std::io::Error> for Error { | io::ErrorKind::NotConnected => ErrorKind::Disconnected, _ => ErrorKind::Failed, }; - Self { - description: format!("{err}"), + #[cfg(feature = "alloc")] + return Self { kind, - } + extra: format!("{err}"), + }; + #[cfg(not(feature = "alloc"))] + return Self { kind }; } } +#[cfg(feature = "alloc")] impl core::convert::From for Error { fn from(err: alloc::string::FromUtf8Error) -> Self { Self::failed(format!("{err}")) } } +#[cfg(feature = "alloc")] impl core::convert::From for Error { fn from(err: alloc::str::Utf8Error) -> Self { Self::failed(format!("{err}")) @@ -257,23 +487,105 @@ impl core::convert::From for Error { impl core::convert::From for Error { fn from(e: NotInSchema) -> Self { - Self::failed(format!( - "Enum value or union discriminant {} was not present in schema.", - e.0 - )) + Self::from_kind(ErrorKind::EnumValueOrUnionDiscriminantNotPresent(e)) + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> { + match self { + Self::Failed => write!(fmt, "Failed"), + Self::Overloaded => write!(fmt, "Overloaded"), + Self::Disconnected => write!(fmt, "Disconnected"), + Self::Unimplemented => write!(fmt, "Unimplemented"), + Self::BufferNotLargeEnough => write!(fmt, "buffer is not large enough"), + Self::ExistingListPointerIsNotByteSized => write!(fmt, "Called get_writable_{{data|text}}_pointer() but existing list pointer is not byte-sized."), + Self::ExistingPointerIsNotAList => write!(fmt, "Called get_writable_{{data|text|list|struct_list}}_pointer() but existing pointer is not a list."), + Self::CannotCreateACanonicalMessageWithACapability => write!(fmt, "Cannot create a canonical message with a capability"), + Self::FourByteLengthTooBigForUSize => write!(fmt, "Cannot represent 4 byte length as `usize`. This may indicate that you are running on 8 or 16 bit platform or message is too large."), + Self::FourByteSegmentLengthTooBigForUSize => write!(fmt, "Cannot represent 4 byte segment length as usize. This may indicate that you are running on 8 or 16 bit platform or segment is too large"), + Self::CannotSetAnyPointerFieldToAPrimitiveValue => write!(fmt, "cannot set AnyPointer field to a primitive value"), + Self::CantHandleNonStructInlineComposite => write!(fmt, "Don't know how to handle non-STRUCT inline composite."), + Self::EmptyBuffer => write!(fmt, "empty buffer"), + Self::EmptySlice => write!(fmt, "empty slice"), + Self::EnumValueOrUnionDiscriminantNotPresent(val) => write!(fmt, "Enum value or union discriminant {val} was not present in schema"), + Self::ExistingListValueIsIncompatibleWithExpectedType => write!(fmt, "Existing list value is incompatible with expected type."), + Self::ExpectedAListOrBlob => write!(fmt, "Expected a list or blob."), + Self::ExpectedAPointerListButGotAListOfDataOnlyStructs => write!(fmt, "Expected a pointer list, but got a list of data-only structs"), + Self::ExpectedAPrimitiveListButGotAListOfPointerOnlyStructs => write!(fmt, "Expected a primitive list, but got a list of pointer-only structs"), + Self::FailedToFillTheWholeBuffer => write!(fmt, "failed to fill the whole buffer"), + Self::FieldAndDefaultMismatch => write!(fmt, "field and default mismatch"), + Self::FieldNotFound => write!(fmt, "field not found"), + Self::FoundBitListWhereStructListWasExpected => write!(fmt, "Found bit list where struct list was expected; upgrading boolean lists to struct lists is no longer supported."), + Self::FoundStructListWhereBitListWasExpected => write!(fmt, "Found struct list where bit list was expected."), + Self::GroupFieldButTypeIsNotStruct => write!(fmt, "group field but type is not Struct"), + Self::InitIsOnlyValidForStructAndAnyPointerFields => write!(fmt, "init() is only valid for struct and AnyPointer fields"), + Self::InitnIsOnlyValidForListTextOrDataFields => write!(fmt, "initn() is only valid for list, text, or data fields"), + Self::InlineCompositeListWithNonStructElementsNotSupported => write!(fmt, "InlineComposite list with non-STRUCT elements not supported."), + Self::InlineCompositeListsElementsOverrunItsWordCount => write!(fmt, "InlineComposite list's elements overrun its word count."), + Self::InlineCompositeListsOfNonStructTypeAreNotSupported => write!(fmt, "InlineComposite lists of non-STRUCT type are not supported."), + Self::InvalidNumberOfSegments(segment_count) => write!(fmt, "Too many or too few segments {segment_count}"), + Self::InvalidSegmentId(id) => write!(fmt, "Invalid segment id {id}"), + Self::ListAnyPointerNotSupported => write!(fmt, "List(AnyPointer) not supported."), + Self::ListCapabilityNotSupported => write!(fmt, "List(Capability) not supported"), + Self::MalformedDoubleFarPointer => write!(fmt, "Malformed double-far pointer."), + Self::MessageContainsInvalidCapabilityPointer => write!(fmt, "Message contained invalid capability pointer."), + Self::MessageContainsListPointerOfNonBytesWhereDataWasExpected => write!(fmt, "Message contains list pointer of non-bytes where data was expected."), + Self::MessageContainsListPointerOfNonBytesWhereTextWasExpected => write!(fmt, "Message contains list pointer of non-bytes where text was expected."), + Self::MessageContainsListWithIncompatibleElementType => write!(fmt, "Message contains list with incompatible element type."), + Self::MessageContainsNonCapabilityPointerWhereCapabilityPointerWasExpected => write!(fmt, "Message contains non-capability pointer where capability pointer was expected."), + Self::MessageContainsNonListPointerWhereDataWasExpected => write!(fmt, "Message contains non-list pointer where data was expected."), + Self::MessageContainsNonListPointerWhereListPointerWasExpected => write!(fmt, "Message contains non-list pointer where list pointer was expected"), + Self::MessageContainsNonListPointerWhereTextWasExpected => write!(fmt, "Message contains non-list pointer where text was expected."), + Self::MessageContainsNonStructPointerWhereStructPointerWasExpected => write!(fmt, "Message contains non-struct pointer where struct pointer was expected."), + Self::MessageContainsNullCapabilityPointer => write!(fmt, "Message contains null capability pointer."), + Self::MessageContainsOutOfBoundsPointer => write!(fmt, "Message contains out-of-bounds pointer"), + Self::MessageContainsTextThatIsNotNULTerminated => write!(fmt, "Message contains text that is not NUL-terminated"), + Self::MessageEndsPrematurely(header, body) => write!(fmt, "Message ends prematurely. Header claimed {header} words, but message only has {body} words"), + Self::MessageIsTooDeeplyNested => write!(fmt, "Message is too deeply nested."), + Self::MessageIsTooDeeplyNestedOrContainsCycles => write!(fmt, "Message is too deeply-nested or contains cycles."), + Self::MessageSizeOverflow => write!(fmt, "Message's size cannot be represented in usize"), + Self::MessageTooLarge(val) => write!(fmt, "Message is too large: {val}"), + Self::MessageNotAlignedBy8BytesBoundary => write!(fmt, "Message was not aligned by 8 bytes boundary. Either ensure that message is properly aligned or compile `capnp` crate with \"unaligned\" feature enabled."), + Self::NestingLimitExceeded => write!(fmt, "nesting limit exceeded"), + Self::NotAStruct => write!(fmt, "not a struct"), + Self::OnlyOneOfTheSectionPointersIsPointingToOurself => write!(fmt, "Only one of the section pointers is pointing to ourself"), + Self::PackedInputDidNotEndCleanlyOnASegmentBoundary => write!(fmt, "Packed input did not end cleanly on a segment boundary."), + Self::PrematureEndOfFile => write!(fmt, "Premature end of file"), + Self::PrematureEndOfPackedInput => write!(fmt, "Premature end of packed input."), + Self::ReadLimitExceeded => write!(fmt, "Read limit exceeded"), + Self::SettingDynamicCapabilitiesIsUnsupported => write!(fmt, "setting dynamic capabilities is unsupported"), + Self::StructReaderHadBitwidthOtherThan1 => write!(fmt, "struct reader had bitwidth other than 1"), + Self::TextBlobMissingNULTerminator => write!(fmt, "Text blob missing NUL terminator."), + Self::TextContainsNonUtf8Data(e) => write!(fmt, "Text contains non-utf8 data: {e}"), + Self::TriedToReadFromNullArena => write!(fmt, "Tried to read from null arena"), + Self::TypeMismatch => write!(fmt, "type mismatch"), + Self::UnalignedSegment => write!(fmt, "Detected unaligned segment. You must either ensure all of your segments are 8-byte aligned, or you must enable the \"unaligned\" feature in the capnp crate"), + Self::UnexepectedFarPointer => write!(fmt, "Unexpected far pointer"), + Self::UnknownPointerType => write!(fmt, "Unknown pointer type."), + } } } impl core::fmt::Display for Error { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> { - write!(fmt, "{:?}: {}", self.kind, self.description) + #[cfg(feature = "alloc")] + let result = if self.extra.is_empty() { + write!(fmt, "{}", self.kind) + } else { + write!(fmt, "{}: {}", self.kind, self.extra) + }; + #[cfg(not(feature = "alloc"))] + let result = write!(fmt, "{}", self.kind); + result } } #[cfg(feature = "std")] impl ::std::error::Error for Error { + #[cfg(feature = "alloc")] fn description(&self) -> &str { - &self.description + &self.extra } fn cause(&self) -> Option<&dyn (::std::error::Error)> { None @@ -282,11 +594,13 @@ impl ::std::error::Error for Error { /// Helper struct that allows `MessageBuilder::get_segments_for_output()` to avoid heap allocations /// in the single-segment case. +#[cfg(feature = "alloc")] pub enum OutputSegments<'a> { SingleSegment([&'a [u8]; 1]), MultiSegment(Vec<&'a [u8]>), } +#[cfg(feature = "alloc")] impl<'a> core::ops::Deref for OutputSegments<'a> { type Target = [&'a [u8]]; fn deref(&self) -> &[&'a [u8]] { @@ -297,6 +611,7 @@ impl<'a> core::ops::Deref for OutputSegments<'a> { } } +#[cfg(feature = "alloc")] impl<'s> message::ReaderSegments for OutputSegments<'s> { fn get_segment(&self, id: u32) -> Option<&[u8]> { match self { diff --git a/capnp/src/list_list.rs b/capnp/src/list_list.rs index 9c7349637..c55f921de 100644 --- a/capnp/src/list_list.rs +++ b/capnp/src/list_list.rs @@ -280,6 +280,7 @@ where } } +#[cfg(feature = "alloc")] impl<'a, T: crate::traits::Owned> From> for crate::dynamic_value::Reader<'a> { fn from(t: Reader<'a, T>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::List(crate::dynamic_list::Reader::new( @@ -289,6 +290,7 @@ impl<'a, T: crate::traits::Owned> From> for crate::dynamic_value:: } } +#[cfg(feature = "alloc")] impl<'a, T: crate::traits::Owned> From> for crate::dynamic_value::Builder<'a> { fn from(t: Builder<'a, T>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::List(crate::dynamic_list::Builder::new( diff --git a/capnp/src/message.rs b/capnp/src/message.rs index 7445b4e30..fe40e0a7c 100644 --- a/capnp/src/message.rs +++ b/capnp/src/message.rs @@ -68,15 +68,22 @@ //! } //! //! ``` +#[cfg(feature = "alloc")] use alloc::vec::Vec; use core::convert::From; use crate::any_pointer; -use crate::private::arena::{BuilderArena, BuilderArenaImpl, ReaderArena, ReaderArenaImpl}; +#[cfg(feature = "alloc")] +use crate::private::arena::{BuilderArena, BuilderArenaImpl}; +use crate::private::arena::{ReaderArena, ReaderArenaImpl}; use crate::private::layout; use crate::private::units::BYTES_PER_WORD; -use crate::traits::{FromPointerBuilder, FromPointerReader, Owned, SetPointerBuilder}; -use crate::{OutputSegments, Result}; +#[cfg(feature = "alloc")] +use crate::traits::{FromPointerBuilder, SetPointerBuilder}; +use crate::traits::{FromPointerReader, Owned}; +#[cfg(feature = "alloc")] +use crate::OutputSegments; +use crate::Result; /// Options controlling how data is read. #[derive(Clone, Copy, Debug)] @@ -275,6 +282,7 @@ where /// Gets the [canonical](https://capnproto.org/encoding.html#canonicalization) form /// of this message. Works by copying the message twice. For a canonicalization /// method that only requires one copy, see `message::Builder::set_root_canonical()`. + #[cfg(feature = "alloc")] pub fn canonicalize(&self) -> Result> { let root = self.get_root_internal()?; let size = root.target_size()?.word_count + 1; @@ -336,6 +344,7 @@ where } } +#[cfg(feature = "alloc")] impl From> for TypedReader, T> where A: Allocator, @@ -347,6 +356,7 @@ where } } +#[cfg(feature = "alloc")] impl From> for TypedReader, T> where A: Allocator, @@ -394,6 +404,7 @@ pub unsafe trait Allocator { } /// A container used to build a message. +#[cfg(feature = "alloc")] pub struct Builder where A: Allocator, @@ -401,8 +412,10 @@ where arena: BuilderArenaImpl, } +#[cfg(feature = "alloc")] unsafe impl Send for Builder where A: Send + Allocator {} +#[cfg(feature = "alloc")] fn _assert_kinds() { fn _assert_send() {} fn _assert_reader() { @@ -413,6 +426,7 @@ fn _assert_kinds() { } } +#[cfg(feature = "alloc")] impl Builder where A: Allocator, @@ -519,6 +533,7 @@ where } } +#[cfg(feature = "alloc")] impl ReaderSegments for Builder where A: Allocator, @@ -538,6 +553,7 @@ where /// - `T` - type of the capnp message which this builder is specialized on. Please see /// [module documentation](self) for more info about builder type specialization. /// - `A` - type of allocator +#[cfg(feature = "alloc")] pub struct TypedBuilder where T: Owned, @@ -547,6 +563,7 @@ where message: Builder, } +#[cfg(feature = "alloc")] impl TypedBuilder where T: Owned, @@ -556,6 +573,7 @@ where } } +#[cfg(feature = "alloc")] impl TypedBuilder where T: Owned, @@ -605,6 +623,7 @@ where } } +#[cfg(feature = "alloc")] impl From> for TypedBuilder where T: Owned, @@ -617,6 +636,7 @@ where /// Standard segment allocator. Allocates each segment via `alloc::alloc::alloc_zeroed()`. #[derive(Debug)] +#[cfg(feature = "alloc")] pub struct HeapAllocator { // Minimum number of words in the next allocation. next_size: u32, @@ -641,6 +661,7 @@ pub enum AllocationStrategy { pub const SUGGESTED_FIRST_SEGMENT_WORDS: u32 = 1024; pub const SUGGESTED_ALLOCATION_STRATEGY: AllocationStrategy = AllocationStrategy::GrowHeuristically; +#[cfg(feature = "alloc")] impl Default for HeapAllocator { fn default() -> Self { Self { @@ -651,6 +672,7 @@ impl Default for HeapAllocator { } } +#[cfg(feature = "alloc")] impl HeapAllocator { pub fn new() -> Self { Self::default() @@ -677,6 +699,7 @@ impl HeapAllocator { } } +#[cfg(feature = "alloc")] unsafe impl Allocator for HeapAllocator { fn allocate_segment(&mut self, minimum_size: u32) -> (*mut u8, u32) { let size = core::cmp::max(minimum_size, self.next_size); @@ -711,6 +734,7 @@ unsafe impl Allocator for HeapAllocator { } } +#[cfg(feature = "alloc")] #[test] fn test_allocate_max() { let allocation_size = 1 << 24; @@ -735,6 +759,7 @@ fn test_allocate_max() { } } +#[cfg(feature = "alloc")] impl Builder { /// Constructs a new `message::Builder` whose first segment has length /// `SUGGESTED_FIRST_SEGMENT_WORDS`. @@ -753,12 +778,14 @@ impl Builder { /// You can reuse a `ScratchSpaceHeapAllocator` by calling `message::Builder::into_allocator()`, /// or by initally passing it to `message::Builder::new()` as a `&mut ScratchSpaceHeapAllocator`. /// Such reuse can save significant amounts of zeroing. +#[cfg(feature = "alloc")] pub struct ScratchSpaceHeapAllocator<'a> { scratch_space: &'a mut [u8], scratch_space_allocated: bool, allocator: HeapAllocator, } +#[cfg(feature = "alloc")] impl<'a> ScratchSpaceHeapAllocator<'a> { /// Writes zeroes into the entire buffer and constructs a new allocator from it. /// @@ -805,6 +832,7 @@ impl<'a> ScratchSpaceHeapAllocator<'a> { } } +#[cfg(feature = "alloc")] unsafe impl<'a> Allocator for ScratchSpaceHeapAllocator<'a> { fn allocate_segment(&mut self, minimum_size: u32) -> (*mut u8, u32) { if (minimum_size as usize) < (self.scratch_space.len() / BYTES_PER_WORD) @@ -835,6 +863,7 @@ unsafe impl<'a> Allocator for ScratchSpaceHeapAllocator<'a> { } } +#[cfg(feature = "alloc")] unsafe impl<'a, A> Allocator for &'a mut A where A: Allocator, diff --git a/capnp/src/primitive_list.rs b/capnp/src/primitive_list.rs index 9468c05b5..a304b90c2 100644 --- a/capnp/src/primitive_list.rs +++ b/capnp/src/primitive_list.rs @@ -256,6 +256,7 @@ where } } +#[cfg(feature = "alloc")] impl<'a, T: PrimitiveElement + crate::introspect::Introspect> From> for crate::dynamic_value::Reader<'a> { @@ -267,6 +268,7 @@ impl<'a, T: PrimitiveElement + crate::introspect::Introspect> From } } +#[cfg(feature = "alloc")] impl<'a, T: PrimitiveElement + crate::introspect::Introspect> From> for crate::dynamic_value::Builder<'a> { diff --git a/capnp/src/private/arena.rs b/capnp/src/private/arena.rs index e1fb57d12..46626c1f0 100644 --- a/capnp/src/private/arena.rs +++ b/capnp/src/private/arena.rs @@ -18,16 +18,21 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -use alloc::string::String; +#[cfg(feature = "alloc")] use alloc::vec::Vec; +#[cfg(feature = "alloc")] use core::slice; use core::u64; use crate::message; -use crate::message::{Allocator, ReaderSegments}; +#[cfg(feature = "alloc")] +use crate::message::Allocator; +use crate::message::ReaderSegments; use crate::private::read_limiter::ReadLimiter; use crate::private::units::*; -use crate::{Error, OutputSegments, Result}; +#[cfg(feature = "alloc")] +use crate::OutputSegments; +use crate::{Error, ErrorKind, Result}; pub type SegmentId = u32; @@ -93,16 +98,13 @@ where #[cfg(not(feature = "unaligned"))] { if seg.as_ptr() as usize % BYTES_PER_WORD != 0 { - return Err(Error::failed( - String::from("Detected unaligned segment. You must either ensure all of your \ - segments are 8-byte aligned, or you must enable the \"unaligned\" \ - feature in the capnp crate"))); + return Err(Error::from_kind(ErrorKind::UnalignedSegment)); } } Ok((seg.as_ptr(), (seg.len() / BYTES_PER_WORD) as u32)) } - None => Err(Error::failed(format!("Invalid segment id: {id}"))), + None => Err(Error::from_kind(ErrorKind::InvalidSegmentId(id))), } } @@ -119,9 +121,9 @@ where let start_idx = start as usize; if start_idx < this_start || ((start_idx - this_start) as i64 + offset) as usize > this_size { - Err(Error::failed(String::from( - "message contained out-of-bounds pointer", - ))) + Err(Error::from_kind( + ErrorKind::MessageContainsOutOfBoundsPointer, + )) } else { unsafe { Ok(start.offset(offset as isize)) } } @@ -135,9 +137,9 @@ where let size = size_in_words * BYTES_PER_WORD; if !(start >= this_start && start - this_start + size <= this_size) { - Err(Error::failed(String::from( - "message contained out-of-bounds pointer", - ))) + Err(Error::from_kind( + ErrorKind::MessageContainsOutOfBoundsPointer, + )) } else { self.read_limiter.can_read(size_in_words) } @@ -161,6 +163,7 @@ pub trait BuilderArena: ReaderArena { } /// A wrapper around a memory segment used in building a message. +#[cfg(feature = "alloc")] struct BuilderSegment { /// Pointer to the start of the segment. ptr: *mut u8, @@ -173,6 +176,7 @@ struct BuilderSegment { allocated: u32, } +#[cfg(feature = "alloc")] pub struct BuilderArenaImplInner where A: Allocator, @@ -183,6 +187,7 @@ where segments: Vec, } +#[cfg(feature = "alloc")] pub struct BuilderArenaImpl where A: Allocator, @@ -190,6 +195,7 @@ where inner: BuilderArenaImplInner, } +#[cfg(feature = "alloc")] impl BuilderArenaImpl where A: Allocator, @@ -252,6 +258,7 @@ where } } +#[cfg(feature = "alloc")] impl ReaderArena for BuilderArenaImpl where A: Allocator, @@ -283,6 +290,7 @@ where } } +#[cfg(feature = "alloc")] impl BuilderArenaImplInner where A: Allocator, @@ -347,6 +355,7 @@ where } } +#[cfg(feature = "alloc")] impl BuilderArena for BuilderArenaImpl where A: Allocator, @@ -368,6 +377,7 @@ where } } +#[cfg(feature = "alloc")] impl Drop for BuilderArenaImplInner where A: Allocator, @@ -381,7 +391,7 @@ pub struct NullArena; impl ReaderArena for NullArena { fn get_segment(&self, _id: u32) -> Result<(*const u8, u32)> { - Err(Error::failed(String::from("tried to read from null arena"))) + Err(Error::from_kind(ErrorKind::TriedToReadFromNullArena)) } unsafe fn check_offset( diff --git a/capnp/src/private/capability.rs b/capnp/src/private/capability.rs index da40c77bd..ba725fb60 100644 --- a/capnp/src/private/capability.rs +++ b/capnp/src/private/capability.rs @@ -19,6 +19,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#![cfg(feature = "alloc")] use alloc::boxed::Box; use alloc::vec::Vec; diff --git a/capnp/src/private/layout.rs b/capnp/src/private/layout.rs index 6633bd452..e071871f6 100644 --- a/capnp/src/private/layout.rs +++ b/capnp/src/private/layout.rs @@ -19,22 +19,22 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -use alloc::boxed::Box; -use alloc::string::String; -use alloc::vec::Vec; +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec::Vec}; use core::cell::Cell; use core::mem; use core::ptr; use crate::data; use crate::private::arena::{BuilderArena, NullArena, ReaderArena, SegmentId}; +#[cfg(feature = "alloc")] use crate::private::capability::ClientHook; use crate::private::mask::Mask; use crate::private::primitive::{Primitive, WireValue}; use crate::private::units::*; use crate::private::zero; use crate::text; -use crate::{MessageSize, Result}; +use crate::{Error, ErrorKind, MessageSize, Result}; pub use self::ElementSize::{ Bit, Byte, EightBytes, FourBytes, InlineComposite, Pointer, TwoBytes, Void, @@ -345,22 +345,25 @@ impl WirePointer { } mod wire_helpers { + #[cfg(feature = "alloc")] use alloc::boxed::Box; - use alloc::string::ToString; use core::{ptr, slice}; use crate::data; use crate::private::arena::*; + #[cfg(feature = "alloc")] use crate::private::capability::ClientHook; use crate::private::layout::ElementSize::*; use crate::private::layout::{data_bits_per_element, pointers_per_element}; + #[cfg(feature = "alloc")] + use crate::private::layout::{CapTableBuilder, CapTableReader}; use crate::private::layout::{ - CapTableBuilder, CapTableReader, ElementSize, ListBuilder, ListReader, StructBuilder, - StructReader, StructSize, WirePointer, WirePointerKind, + ElementSize, ListBuilder, ListReader, StructBuilder, StructReader, StructSize, WirePointer, + WirePointerKind, }; use crate::private::units::*; use crate::text; - use crate::{Error, MessageSize, Result}; + use crate::{Error, ErrorKind, MessageSize, Result}; pub struct SegmentAnd { #[allow(dead_code)] @@ -699,7 +702,7 @@ mod wire_helpers { }; if nesting_limit <= 0 { - return Err(Error::failed("Message is too deeply nested.".to_string())); + return Err(Error::from_kind(ErrorKind::MessageIsTooDeeplyNested)); } nesting_limit -= 1; @@ -778,17 +781,16 @@ mod wire_helpers { let count = (*element_tag).inline_composite_list_element_count(); if (*element_tag).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "Don't know how to handle non-STRUCT inline composite.".to_string(), + return Err(Error::from_kind( + ErrorKind::CantHandleNonStructInlineComposite, )); } let actual_size = u64::from((*element_tag).struct_word_size()) * u64::from(count); if actual_size > u64::from(word_count) { - return Err(Error::failed( - "InlineComposite list's elements overrun its word count." - .to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListsElementsOverrunItsWordCount, )); } @@ -819,13 +821,13 @@ mod wire_helpers { } } WirePointerKind::Far => { - return Err(Error::failed("Malformed double-far pointer.".to_string())); + return Err(Error::from_kind(ErrorKind::MalformedDoubleFarPointer)); } WirePointerKind::Other => { if (*reff).is_capability() { result.cap_count += 1; } else { - return Err(Error::failed("Unknown pointer type.".to_string())); + return Err(Error::from_kind(ErrorKind::UnknownPointerType)); } } } @@ -837,7 +839,7 @@ mod wire_helpers { unsafe fn copy_struct( arena: &mut dyn BuilderArena, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, dst: *mut u8, src: *const u8, data_size: isize, @@ -852,6 +854,7 @@ mod wire_helpers { copy_message( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, dst_refs.offset(ii), src_refs.offset(ii), @@ -864,7 +867,7 @@ mod wire_helpers { pub unsafe fn copy_message( arena: &mut dyn BuilderArena, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, dst: *mut WirePointer, src: *const WirePointer, ) -> (*mut u8, *mut WirePointer, u32) { @@ -885,6 +888,7 @@ mod wire_helpers { copy_struct( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, dst_ptr, src_ptr, @@ -937,6 +941,7 @@ mod wire_helpers { copy_message( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, dst_refs.offset(ii * BYTES_PER_WORD as isize) as *mut WirePointer, src_refs.offset(ii), @@ -971,6 +976,7 @@ mod wire_helpers { copy_struct( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, dst_element, src_element, @@ -1110,7 +1116,7 @@ mod wire_helpers { arena: &mut dyn BuilderArena, reff: *mut WirePointer, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, size: StructSize, ) -> StructBuilder<'_> { let (ptr, reff, segment_id) = allocate( @@ -1125,6 +1131,7 @@ mod wire_helpers { StructBuilder { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, data: ptr as *mut _, pointers: ptr.offset((size.data as usize) as isize * BYTES_PER_WORD as isize) as *mut _, @@ -1138,7 +1145,7 @@ mod wire_helpers { arena: &'a mut dyn BuilderArena, mut reff: *mut WirePointer, mut segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, size: StructSize, default: Option<&'a [crate::Word]>, ) -> Result> { @@ -1148,18 +1155,29 @@ mod wire_helpers { match default { None => { return Ok(init_struct_pointer( - arena, reff, segment_id, cap_table, size, + arena, + reff, + segment_id, + #[cfg(feature = "alloc")] + cap_table, + size, )) } Some(d) if (*(d.as_ptr() as *const WirePointer)).is_null() => { return Ok(init_struct_pointer( - arena, reff, segment_id, cap_table, size, + arena, + reff, + segment_id, + #[cfg(feature = "alloc")] + cap_table, + size, )) } Some(d) => { let (new_ref_target, new_reff, new_segment_id) = copy_message( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, reff, d.as_ptr() as *const WirePointer, @@ -1174,9 +1192,8 @@ mod wire_helpers { let (old_ptr, old_ref, old_segment_id) = follow_builder_fars(arena, reff, ref_target, segment_id)?; if (*old_ref).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "Message contains non-struct pointer where struct pointer was expected." - .to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsNonStructPointerWhereStructPointerWasExpected, )); } @@ -1230,6 +1247,7 @@ mod wire_helpers { Ok(StructBuilder { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, data: ptr as *mut _, pointers: new_pointer_section, @@ -1240,6 +1258,7 @@ mod wire_helpers { Ok(StructBuilder { arena, segment_id: old_segment_id, + #[cfg(feature = "alloc")] cap_table, data: old_ptr, pointers: old_pointer_section, @@ -1254,7 +1273,7 @@ mod wire_helpers { arena: &mut dyn BuilderArena, reff: *mut WirePointer, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, element_count: ElementCount32, element_size: ElementSize, ) -> ListBuilder<'_> { @@ -1275,6 +1294,7 @@ mod wire_helpers { ListBuilder { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, ptr, step, @@ -1290,7 +1310,7 @@ mod wire_helpers { arena: &mut dyn BuilderArena, reff: *mut WirePointer, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, element_count: ElementCount32, element_size: StructSize, ) -> ListBuilder<'_> { @@ -1320,6 +1340,7 @@ mod wire_helpers { ListBuilder { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: ptr1 as *mut _, step: words_per_element * BITS_PER_WORD as u32, @@ -1335,7 +1356,7 @@ mod wire_helpers { arena: &mut dyn BuilderArena, mut orig_ref: *mut WirePointer, mut orig_segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, element_size: ElementSize, default_value: *const u8, ) -> Result> { @@ -1353,6 +1374,7 @@ mod wire_helpers { let (new_orig_ref_target, new_orig_ref, new_orig_segment_id) = copy_message( arena, orig_segment_id, + #[cfg(feature = "alloc")] cap_table, orig_ref, default_value as *const WirePointer, @@ -1371,10 +1393,7 @@ mod wire_helpers { follow_builder_fars(arena, orig_ref, orig_ref_target, orig_segment_id)?; if (*reff).kind() != WirePointerKind::List { - return Err(Error::failed( - "Called get_writable_list_pointer() but existing pointer is not a list." - .to_string(), - )); + return Err(Error::from_kind(ErrorKind::ExistingPointerIsNotAList)); } let old_size = (*reff).list_element_size(); @@ -1390,8 +1409,8 @@ mod wire_helpers { let tag: *const WirePointer = ptr as *const _; if (*tag).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "InlineComposite list with non-STRUCT elements not supported.".to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListWithNonStructElementsNotSupported, )); } @@ -1403,21 +1422,21 @@ mod wire_helpers { match element_size { Void => {} // Anything is a valid upgrade from Void. Bit => { - return Err(Error::failed( - "Found struct list where bit list was expected.".to_string(), + return Err(Error::from_kind( + ErrorKind::FoundStructListWhereBitListWasExpected, )); } Byte | TwoBytes | FourBytes | EightBytes => { if data_size < 1 { - return Err(Error::failed( - "Existing list value is incompatible with expected type.".to_string(), + return Err(Error::from_kind( + ErrorKind::ExistingListValueIsIncompatibleWithExpectedType, )); } } Pointer => { if pointer_count < 1 { - return Err(Error::failed( - "Existing list value is incompatible with expected type.".to_string(), + return Err(Error::from_kind( + ErrorKind::ExistingListValueIsIncompatibleWithExpectedType, )); } // Adjust the pointer to point at the reference segment. @@ -1432,6 +1451,7 @@ mod wire_helpers { Ok(ListBuilder { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: ptr as *mut _, element_count: (*tag).inline_composite_list_element_count(), @@ -1447,8 +1467,8 @@ mod wire_helpers { if data_size < data_bits_per_element(element_size) || pointer_count < pointers_per_element(element_size) { - return Err(Error::failed( - "Existing list value is incompatible with expected type.".to_string(), + return Err(Error::from_kind( + ErrorKind::ExistingListValueIsIncompatibleWithExpectedType, )); } @@ -1457,6 +1477,7 @@ mod wire_helpers { Ok(ListBuilder { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: ptr as *mut _, step, @@ -1473,7 +1494,7 @@ mod wire_helpers { arena: &mut dyn BuilderArena, mut orig_ref: *mut WirePointer, mut orig_segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, element_size: StructSize, default_value: *const u8, ) -> Result> { @@ -1486,6 +1507,7 @@ mod wire_helpers { let (new_orig_ref_target, new_orig_ref, new_orig_segment_id) = copy_message( arena, orig_segment_id, + #[cfg(feature = "alloc")] cap_table, orig_ref, default_value as *const WirePointer, @@ -1501,10 +1523,7 @@ mod wire_helpers { follow_builder_fars(arena, orig_ref, orig_ref_target, orig_segment_id)?; if (*old_ref).kind() != WirePointerKind::List { - return Err(Error::failed( - "Called get_writable_struct_list_pointer() but existing pointer is not a list." - .to_string(), - )); + return Err(Error::from_kind(ErrorKind::ExistingPointerIsNotAList)); } let old_size = (*old_ref).list_element_size(); @@ -1515,8 +1534,8 @@ mod wire_helpers { let old_tag: *const WirePointer = old_ptr as *const _; old_ptr = old_ptr.add(BYTES_PER_WORD); if (*old_tag).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "InlineComposite list with non-STRUCT elements not supported.".to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListWithNonStructElementsNotSupported, )); } @@ -1531,6 +1550,7 @@ mod wire_helpers { return Ok(ListBuilder { arena, segment_id: old_segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: old_ptr as *mut _, element_count, @@ -1602,6 +1622,7 @@ mod wire_helpers { Ok(ListBuilder { arena, segment_id: new_segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: new_ptr, element_count, @@ -1624,6 +1645,7 @@ mod wire_helpers { arena, orig_ref, orig_segment_id, + #[cfg(feature = "alloc")] cap_table, element_count, element_size, @@ -1632,10 +1654,8 @@ mod wire_helpers { // Upgrade to an inline composite list. if old_size == ElementSize::Bit { - return Err(Error::failed( - "Found bit list where struct list was expected; upgrading boolean \ - lists to struct lists is no longer supported." - .to_string(), + return Err(Error::from_kind( + ErrorKind::FoundBitListWhereStructListWasExpected, )); } @@ -1702,6 +1722,7 @@ mod wire_helpers { Ok(ListBuilder { arena, segment_id: new_segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: new_ptr, element_count, @@ -1771,6 +1792,7 @@ mod wire_helpers { let (new_ref_target, new_reff, new_segment_id) = copy_message( arena, segment_id, + #[cfg(feature = "alloc")] CapTableBuilder::Plain(::core::ptr::null_mut()), reff, d.as_ptr() as *const _, @@ -1787,23 +1809,17 @@ mod wire_helpers { let (ptr, reff, _segment_id) = follow_builder_fars(arena, reff, ref_target, segment_id)?; if (*reff).kind() != WirePointerKind::List { - return Err(Error::failed( - "Called get_writable_text_pointer() but existing pointer is not a list." - .to_string(), - )); + return Err(Error::from_kind(ErrorKind::ExistingPointerIsNotAList)); } if (*reff).list_element_size() != Byte { - return Err(Error::failed( - "Called get_writable_text_pointer() but existing list pointer is not byte-sized." - .to_string(), + return Err(Error::from_kind( + ErrorKind::ExistingListPointerIsNotByteSized, )); } let count = (*reff).list_element_count(); if count == 0 || *ptr.offset((count - 1) as isize) != 0 { - return Err(Error::failed( - "Text blob missing NUL terminator.".to_string(), - )); + return Err(Error::from_kind(ErrorKind::TextBlobMissingNULTerminator)); } // Subtract 1 from the size for the NUL terminator. @@ -1864,6 +1880,7 @@ mod wire_helpers { let (new_ref_target, new_reff, new_segment_id) = copy_message( arena, segment_id, + #[cfg(feature = "alloc")] CapTableBuilder::Plain(core::ptr::null_mut()), reff, d.as_ptr() as *const _, @@ -1880,15 +1897,11 @@ mod wire_helpers { let (ptr, reff, _segment_id) = follow_builder_fars(arena, reff, ref_target, segment_id)?; if (*reff).kind() != WirePointerKind::List { - return Err(Error::failed( - "Called get_writable_data_pointer() but existing pointer is not a list." - .to_string(), - )); + return Err(Error::from_kind(ErrorKind::ExistingPointerIsNotAList)); } if (*reff).list_element_size() != Byte { - return Err(Error::failed( - "Called get_writable_data_pointer() but existing list pointer is not byte-sized." - .to_string(), + return Err(Error::from_kind( + ErrorKind::ExistingListPointerIsNotByteSized, )); } @@ -1901,7 +1914,7 @@ mod wire_helpers { pub unsafe fn set_struct_pointer( arena: &mut dyn BuilderArena, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, reff: *mut WirePointer, value: StructReader, canonicalize: bool, @@ -1912,8 +1925,8 @@ mod wire_helpers { if canonicalize { // StructReaders should not have bitwidths other than 1, but let's be safe if !(value.data_size == 1 || value.data_size % BITS_PER_BYTE as u32 == 0) { - return Err(Error::failed( - "struct reader had bitwidth other than 1".to_string(), + return Err(Error::from_kind( + ErrorKind::StructReaderHadBitwidthOtherThan1, )); } @@ -1965,10 +1978,12 @@ mod wire_helpers { copy_pointer( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, pointer_section.offset(i), value.arena, value.segment_id, + #[cfg(feature = "alloc")] value.cap_table, value.pointers.offset(i), value.nesting_limit, @@ -1982,6 +1997,7 @@ mod wire_helpers { }) } + #[cfg(feature = "alloc")] pub fn set_capability_pointer( _arena: &mut dyn BuilderArena, _segment_id: u32, @@ -1998,7 +2014,7 @@ mod wire_helpers { pub unsafe fn set_list_pointer( arena: &mut dyn BuilderArena, segment_id: u32, - cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, reff: *mut WirePointer, value: ListReader, canonicalize: bool, @@ -2018,10 +2034,12 @@ mod wire_helpers { copy_pointer( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, (ptr as *mut WirePointer).offset(i), value.arena, value.segment_id, + #[cfg(feature = "alloc")] value.cap_table, (value.ptr as *const WirePointer).offset(i), value.nesting_limit, @@ -2141,10 +2159,12 @@ mod wire_helpers { copy_pointer( arena, segment_id, + #[cfg(feature = "alloc")] cap_table, dst as *mut _, value.arena, value.segment_id, + #[cfg(feature = "alloc")] value.cap_table, src as *const WirePointer, value.nesting_limit, @@ -2167,11 +2187,11 @@ mod wire_helpers { pub unsafe fn copy_pointer( dst_arena: &mut dyn BuilderArena, dst_segment_id: u32, - dst_cap_table: CapTableBuilder, + #[cfg(feature = "alloc")] dst_cap_table: CapTableBuilder, dst: *mut WirePointer, src_arena: &dyn ReaderArena, src_segment_id: u32, - src_cap_table: CapTableReader, + #[cfg(feature = "alloc")] src_cap_table: CapTableReader, src: *const WirePointer, nesting_limit: i32, canonicalize: bool, @@ -2189,9 +2209,8 @@ mod wire_helpers { match (*src).kind() { WirePointerKind::Struct => { if nesting_limit <= 0 { - return Err(Error::failed( - "Message is too deeply-nested or contains cycles. See ReaderOptions." - .to_string(), + return Err(Error::from_kind( + ErrorKind::MessageIsTooDeeplyNestedOrContainsCycles, )); } @@ -2206,11 +2225,13 @@ mod wire_helpers { set_struct_pointer( dst_arena, dst_segment_id, + #[cfg(feature = "alloc")] dst_cap_table, dst, StructReader { arena: src_arena, segment_id: src_segment_id, + #[cfg(feature = "alloc")] cap_table: src_cap_table, data: ptr, pointers: ptr @@ -2226,9 +2247,8 @@ mod wire_helpers { WirePointerKind::List => { let element_size = (*src).list_element_size(); if nesting_limit <= 0 { - return Err(Error::failed( - "Message is too deeply-nested or contains cycles. See ReaderOptions." - .to_string(), + return Err(Error::from_kind( + ErrorKind::MessageIsTooDeeplyNestedOrContainsCycles, )); } @@ -2246,9 +2266,8 @@ mod wire_helpers { )?; if (*tag).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "InlineComposite lists of non-STRUCT type are not supported." - .to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListsOfNonStructTypeAreNotSupported, )); } @@ -2258,8 +2277,8 @@ mod wire_helpers { if u64::from(words_per_element) * u64::from(element_count) > u64::from(word_count) { - return Err(Error::failed( - "InlineComposite list's elements overrun its word count.".to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListsElementsOverrunItsWordCount, )); } @@ -2272,11 +2291,13 @@ mod wire_helpers { set_list_pointer( dst_arena, dst_segment_id, + #[cfg(feature = "alloc")] dst_cap_table, dst, ListReader { arena: src_arena, segment_id: src_segment_id, + #[cfg(feature = "alloc")] cap_table: src_cap_table, ptr: ptr as *const _, element_count, @@ -2314,11 +2335,13 @@ mod wire_helpers { set_list_pointer( dst_arena, dst_segment_id, + #[cfg(feature = "alloc")] dst_cap_table, dst, ListReader { arena: src_arena, segment_id: src_segment_id, + #[cfg(feature = "alloc")] cap_table: src_cap_table, ptr: ptr as *const _, element_count, @@ -2332,16 +2355,17 @@ mod wire_helpers { ) } } - WirePointerKind::Far => Err(Error::failed("Malformed double-far pointer.".to_string())), + WirePointerKind::Far => Err(Error::from_kind(ErrorKind::MalformedDoubleFarPointer)), WirePointerKind::Other => { if !(*src).is_capability() { - return Err(Error::failed("Unknown pointer type.".to_string())); + return Err(Error::from_kind(ErrorKind::UnknownPointerType)); } if canonicalize { - return Err(Error::failed( - "Cannot create a canonical message with a capability".to_string(), + return Err(Error::from_kind( + ErrorKind::CannotCreateACanonicalMessageWithACapability, )); } + #[cfg(feature = "alloc")] match src_cap_table.extract_cap((*src).cap_index() as usize) { Some(cap) => { set_capability_pointer(dst_arena, dst_segment_id, dst_cap_table, dst, cap); @@ -2350,10 +2374,12 @@ mod wire_helpers { value: ptr::null_mut(), }) } - None => Err(Error::failed( - "Message contained invalid capability pointer.".to_string(), + None => Err(Error::from_kind( + ErrorKind::MessageContainsInvalidCapabilityPointer, )), } + #[cfg(not(feature = "alloc"))] + return Err(Error::from_kind(ErrorKind::UnknownPointerType)); } } } @@ -2362,7 +2388,7 @@ mod wire_helpers { pub unsafe fn read_struct_pointer<'a>( mut arena: &'a dyn ReaderArena, mut segment_id: u32, - cap_table: CapTableReader, + #[cfg(feature = "alloc")] cap_table: CapTableReader, mut reff: *const WirePointer, default: Option<&'a [crate::Word]>, nesting_limit: i32, @@ -2382,8 +2408,8 @@ mod wire_helpers { } if nesting_limit <= 0 { - return Err(Error::failed( - "Message is too deeply-nested or contains cycles.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageIsTooDeeplyNestedOrContainsCycles, )); } @@ -2392,9 +2418,8 @@ mod wire_helpers { let data_size_words = (*reff).struct_data_size(); if (*reff).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "Message contains non-struct pointer where struct pointer was expected." - .to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsNonStructPointerWhereStructPointerWasExpected, )); } @@ -2409,6 +2434,7 @@ mod wire_helpers { Ok(StructReader { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, data: ptr, pointers: ptr.offset(data_size_words as isize * BYTES_PER_WORD as isize) as *const _, @@ -2419,6 +2445,7 @@ mod wire_helpers { } #[inline] + #[cfg(feature = "alloc")] pub unsafe fn read_capability_pointer( _arena: &dyn ReaderArena, _segment_id: u32, @@ -2427,21 +2454,20 @@ mod wire_helpers { _nesting_limit: i32, ) -> Result> { if (*reff).is_null() { - Err(Error::failed( - "Message contains null capability pointer.".to_string(), + Err(Error::from_kind( + ErrorKind::MessageContainsNullCapabilityPointer, )) } else if !(*reff).is_capability() { - Err(Error::failed( - "Message contains non-capability pointer where capability pointer was expected." - .to_string(), + Err(Error::from_kind( + ErrorKind::MessageContainsNonCapabilityPointerWhereCapabilityPointerWasExpected, )) } else { let n = (*reff).cap_index() as usize; match cap_table.extract_cap(n) { Some(client_hook) => Ok(client_hook), - None => Err(Error::failed(format!( - "Message contains invalid capability pointer. Index: {n}" - ))), + None => Err(Error::from_kind( + ErrorKind::MessageContainsInvalidCapabilityPointer, + )), } } } @@ -2450,7 +2476,7 @@ mod wire_helpers { pub unsafe fn read_list_pointer( mut arena: &dyn ReaderArena, mut segment_id: u32, - cap_table: CapTableReader, + #[cfg(feature = "alloc")] cap_table: CapTableReader, mut reff: *const WirePointer, default_value: *const u8, expected_element_size: Option, @@ -2466,13 +2492,13 @@ mod wire_helpers { } if nesting_limit <= 0 { - return Err(Error::failed("nesting limit exceeded".to_string())); + return Err(Error::from_kind(ErrorKind::NestingLimitExceeded)); } let (mut ptr, reff, segment_id) = follow_fars(arena, reff, segment_id)?; if (*reff).kind() != WirePointerKind::List { - return Err(Error::failed( - "Message contains non-list pointer where list pointer was expected".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsNonListPointerWhereListPointerWasExpected, )); } @@ -2494,8 +2520,8 @@ mod wire_helpers { )?; if (*tag).kind() != WirePointerKind::Struct { - return Err(Error::failed( - "InlineComposite lists of non-STRUCT type are not supported.".to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListsOfNonStructTypeAreNotSupported, )); } @@ -2505,8 +2531,8 @@ mod wire_helpers { let words_per_element = (*tag).struct_word_size(); if u64::from(size) * u64::from(words_per_element) > u64::from(word_count) { - return Err(Error::failed( - "InlineComposite list's elements overrun its word count.".to_string(), + return Err(Error::from_kind( + ErrorKind::InlineCompositeListsElementsOverrunItsWordCount, )); } @@ -2525,23 +2551,21 @@ mod wire_helpers { match expected_element_size { None | Some(Void | InlineComposite) => (), Some(Bit) => { - return Err(Error::failed( - "Found struct list where bit list was expected.".to_string(), + return Err(Error::from_kind( + ErrorKind::FoundStructListWhereBitListWasExpected, )); } Some(Byte | TwoBytes | FourBytes | EightBytes) => { if data_size == 0 { - return Err(Error::failed( - "Expected a primitive list, but got a list of pointer-only structs" - .to_string(), + return Err(Error::from_kind( + ErrorKind::ExpectedAPrimitiveListButGotAListOfPointerOnlyStructs, )); } } Some(Pointer) => { if ptr_count == 0 { - return Err(Error::failed( - "Expected a pointer list, but got a list of data-only structs" - .to_string(), + return Err(Error::from_kind( + ErrorKind::ExpectedAPointerListButGotAListOfDataOnlyStructs, )); } } @@ -2550,6 +2574,7 @@ mod wire_helpers { Ok(ListReader { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: ptr as *const _, element_count: size, @@ -2587,9 +2612,9 @@ mod wire_helpers { if let Some(expected_element_size) = expected_element_size { if element_size == ElementSize::Bit && expected_element_size != ElementSize::Bit { - return Err(Error::failed( - "Found bit list where struct list was expected; upgrade boolean lists to\ - structs is no longer supported".to_string())); + return Err(Error::from_kind( + ErrorKind::FoundBitListWhereStructListWasExpected, + )); } // Verify that the elements are at least as large as the expected type. Note that if @@ -2604,8 +2629,8 @@ mod wire_helpers { if expected_data_bits_per_element > data_size || expected_pointers_per_element > pointer_count { - return Err(Error::failed( - "Message contains list with incompatible element type.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsListWithIncompatibleElementType, )); } } @@ -2613,6 +2638,7 @@ mod wire_helpers { Ok(ListReader { arena, segment_id, + #[cfg(feature = "alloc")] cap_table, ptr: ptr as *const _, element_count, @@ -2648,14 +2674,14 @@ mod wire_helpers { let size = (*reff).list_element_count(); if (*reff).kind() != WirePointerKind::List { - return Err(Error::failed( - "Message contains non-list pointer where text was expected.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsNonListPointerWhereTextWasExpected, )); } if (*reff).list_element_size() != Byte { - return Err(Error::failed( - "Message contains list pointer of non-bytes where text was expected.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsListPointerOfNonBytesWhereTextWasExpected, )); } @@ -2668,16 +2694,16 @@ mod wire_helpers { )?; if size == 0 { - return Err(Error::failed( - "Message contains text that is not NUL-terminated.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsTextThatIsNotNULTerminated, )); } let str_ptr = ptr as *const u8; if (*str_ptr.offset((size - 1) as isize)) != 0u8 { - return Err(Error::failed( - "Message contains text that is not NUL-terminated".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsTextThatIsNotNULTerminated, )); } @@ -2707,14 +2733,14 @@ mod wire_helpers { let size: u32 = (*reff).list_element_count(); if (*reff).kind() != WirePointerKind::List { - return Err(Error::failed( - "Message contains non-list pointer where data was expected.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsNonListPointerWhereDataWasExpected, )); } if (*reff).list_element_size() != Byte { - return Err(Error::failed( - "Message contains list pointer of non-bytes where data was expected.".to_string(), + return Err(Error::from_kind( + ErrorKind::MessageContainsListPointerOfNonBytesWhereDataWasExpected, )); } @@ -2737,9 +2763,11 @@ fn zero_pointer() -> *const WirePointer { static NULL_ARENA: NullArena = NullArena; +#[cfg(feature = "alloc")] pub type CapTable = Vec>>; #[derive(Copy, Clone)] +#[cfg(feature = "alloc")] pub enum CapTableReader { // At one point, we had a `Dummy` variant here, but that ended up // making values of this type take 16 bytes of memory. Now we instead @@ -2747,6 +2775,7 @@ pub enum CapTableReader { Plain(*const Vec>>), } +#[cfg(feature = "alloc")] impl CapTableReader { pub fn extract_cap(&self, index: usize) -> Option> { match *self { @@ -2766,6 +2795,7 @@ impl CapTableReader { } #[derive(Copy, Clone)] +#[cfg(feature = "alloc")] pub enum CapTableBuilder { // At one point, we had a `Dummy` variant here, but that ended up // making values of this type take 16 bytes of memory. Now we instead @@ -2773,6 +2803,7 @@ pub enum CapTableBuilder { Plain(*mut Vec>>), } +#[cfg(feature = "alloc")] impl CapTableBuilder { pub fn into_reader(self) -> CapTableReader { match self { @@ -2833,6 +2864,7 @@ impl CapTableBuilder { #[derive(Clone, Copy)] pub struct PointerReader<'a> { arena: &'a dyn ReaderArena, + #[cfg(feature = "alloc")] cap_table: CapTableReader, pointer: *const WirePointer, segment_id: u32, @@ -2844,6 +2876,7 @@ impl<'a> PointerReader<'a> { PointerReader { arena: &NULL_ARENA, segment_id: 0, + #[cfg(feature = "alloc")] cap_table: CapTableReader::Plain(ptr::null()), pointer: ptr::null(), nesting_limit: 0x7fffffff, @@ -2867,6 +2900,7 @@ impl<'a> PointerReader<'a> { Ok(PointerReader { arena, segment_id, + #[cfg(feature = "alloc")] cap_table: CapTableReader::Plain(ptr::null()), pointer: location as *const _, nesting_limit, @@ -2884,12 +2918,14 @@ impl<'a> PointerReader<'a> { PointerReader { arena: &NULL_ARENA, segment_id: 0, + #[cfg(feature = "alloc")] cap_table: CapTableReader::Plain(ptr::null()), pointer: location as *const _, nesting_limit: 0x7fffffff, } } + #[cfg(feature = "alloc")] pub fn imbue(&mut self, cap_table: CapTableReader) { self.cap_table = cap_table; } @@ -2927,6 +2963,7 @@ impl<'a> PointerReader<'a> { wire_helpers::read_struct_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, reff, default, @@ -2953,6 +2990,7 @@ impl<'a> PointerReader<'a> { wire_helpers::read_list_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, reff, default_value, @@ -2972,6 +3010,7 @@ impl<'a> PointerReader<'a> { wire_helpers::read_list_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, reff, default_value, @@ -2999,6 +3038,7 @@ impl<'a> PointerReader<'a> { unsafe { wire_helpers::read_data_pointer(self.arena, self.segment_id, reff, default) } } + #[cfg(feature = "alloc")] pub fn get_capability(&self) -> Result> { let reff: *const WirePointer = if self.pointer.is_null() { zero_pointer() @@ -3024,16 +3064,14 @@ impl<'a> PointerReader<'a> { unsafe { wire_helpers::follow_fars(self.arena, self.pointer, self.segment_id)? }; match unsafe { (*reff).kind() } { - WirePointerKind::Far => { - Err(crate::Error::failed(String::from("Unexpected FAR pointer"))) - } + WirePointerKind::Far => Err(Error::from_kind(ErrorKind::UnexepectedFarPointer)), WirePointerKind::Struct => Ok(PointerType::Struct), WirePointerKind::List => Ok(PointerType::List), WirePointerKind::Other => { if unsafe { (*reff).is_capability() } { Ok(PointerType::Capability) } else { - Err(crate::Error::failed(String::from("Unknown pointer type"))) + Err(Error::from_kind(ErrorKind::UnknownPointerType)) } } } @@ -3071,6 +3109,7 @@ impl<'a> PointerReader<'a> { pub struct PointerBuilder<'a> { arena: &'a mut dyn BuilderArena, segment_id: u32, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, pointer: *mut WirePointer, } @@ -3080,6 +3119,7 @@ impl<'a> PointerBuilder<'a> { pub fn get_root(arena: &'a mut dyn BuilderArena, segment_id: u32, location: *mut u8) -> Self { PointerBuilder { arena, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder::Plain(ptr::null_mut()), segment_id, pointer: location as *mut _, @@ -3094,6 +3134,7 @@ impl<'a> PointerBuilder<'a> { } } + #[cfg(feature = "alloc")] pub fn imbue(&mut self, cap_table: CapTableBuilder) { self.cap_table = cap_table; } @@ -3113,6 +3154,7 @@ impl<'a> PointerBuilder<'a> { self.arena, self.pointer, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, size, default, @@ -3134,6 +3176,7 @@ impl<'a> PointerBuilder<'a> { self.arena, self.pointer, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, element_size, default_value, @@ -3155,6 +3198,7 @@ impl<'a> PointerBuilder<'a> { self.arena, self.pointer, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, element_size, default_value, @@ -3184,6 +3228,7 @@ impl<'a> PointerBuilder<'a> { } } + #[cfg(feature = "alloc")] pub fn get_capability(&self) -> Result> { unsafe { wire_helpers::read_capability_pointer( @@ -3202,6 +3247,7 @@ impl<'a> PointerBuilder<'a> { self.arena, self.pointer, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, size, ) @@ -3218,6 +3264,7 @@ impl<'a> PointerBuilder<'a> { self.arena, self.pointer, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, element_count, element_size, @@ -3235,6 +3282,7 @@ impl<'a> PointerBuilder<'a> { self.arena, self.pointer, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, element_count, element_size, @@ -3259,6 +3307,7 @@ impl<'a> PointerBuilder<'a> { wire_helpers::set_struct_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, self.pointer, *value, @@ -3273,6 +3322,7 @@ impl<'a> PointerBuilder<'a> { wire_helpers::set_list_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, self.pointer, *value, @@ -3294,6 +3344,7 @@ impl<'a> PointerBuilder<'a> { } } + #[cfg(feature = "alloc")] pub fn set_capability(&mut self, cap: Box) { wire_helpers::set_capability_pointer( self.arena, @@ -3317,10 +3368,12 @@ impl<'a> PointerBuilder<'a> { wire_helpers::copy_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, self.pointer, other.arena, other.segment_id, + #[cfg(feature = "alloc")] other.cap_table, other.pointer, other.nesting_limit, @@ -3342,6 +3395,7 @@ impl<'a> PointerBuilder<'a> { PointerReader { arena: self.arena.as_reader(), segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table.into_reader(), pointer: self.pointer, nesting_limit: 0x7fffffff, @@ -3352,6 +3406,7 @@ impl<'a> PointerBuilder<'a> { PointerReader { arena: self.arena.as_reader(), segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table.into_reader(), pointer: self.pointer, nesting_limit: 0x7fffffff, @@ -3362,6 +3417,7 @@ impl<'a> PointerBuilder<'a> { #[derive(Clone, Copy)] pub struct StructReader<'a> { arena: &'a dyn ReaderArena, + #[cfg(feature = "alloc")] cap_table: CapTableReader, data: *const u8, pointers: *const WirePointer, @@ -3376,6 +3432,7 @@ impl<'a> StructReader<'a> { StructReader { arena: &NULL_ARENA, segment_id: 0, + #[cfg(feature = "alloc")] cap_table: CapTableReader::Plain(ptr::null()), data: ptr::null(), pointers: ptr::null(), @@ -3385,6 +3442,7 @@ impl<'a> StructReader<'a> { } } + #[cfg(feature = "alloc")] pub fn imbue(&mut self, cap_table: CapTableReader) { self.cap_table = cap_table } @@ -3401,6 +3459,7 @@ impl<'a> StructReader<'a> { ListReader { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, ptr: self.pointers as *const _, element_count: u32::from(self.pointer_count), @@ -3470,6 +3529,7 @@ impl<'a> StructReader<'a> { PointerReader { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, pointer: unsafe { self.pointers.add(ptr_index) }, nesting_limit: self.nesting_limit, @@ -3570,6 +3630,7 @@ impl<'a> StructReader<'a> { pub struct StructBuilder<'a> { arena: &'a mut dyn BuilderArena, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, data: *mut u8, pointers: *mut WirePointer, @@ -3590,6 +3651,7 @@ impl<'a> StructBuilder<'a> { pub fn as_reader(&self) -> StructReader<'_> { StructReader { arena: self.arena.as_reader(), + #[cfg(feature = "alloc")] cap_table: self.cap_table.into_reader(), data: self.data, pointers: self.pointers, @@ -3603,6 +3665,7 @@ impl<'a> StructBuilder<'a> { pub fn into_reader(self) -> StructReader<'a> { StructReader { arena: self.arena.as_reader(), + #[cfg(feature = "alloc")] cap_table: self.cap_table.into_reader(), data: self.data, pointers: self.pointers, @@ -3613,6 +3676,7 @@ impl<'a> StructBuilder<'a> { } } + #[cfg(feature = "alloc")] pub fn imbue(&mut self, cap_table: CapTableBuilder) { self.cap_table = cap_table } @@ -3680,6 +3744,7 @@ impl<'a> StructBuilder<'a> { PointerBuilder { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, pointer: unsafe { self.pointers.add(ptr_index) }, } @@ -3690,6 +3755,7 @@ impl<'a> StructBuilder<'a> { PointerBuilder { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, pointer: unsafe { self.pointers.add(ptr_index) }, } @@ -3714,9 +3780,9 @@ impl<'a> StructBuilder<'a> { if (shared_data_size == 0 || other.data == self.data) && (shared_pointer_count == 0 || other.pointers == self.pointers) { - return Err(crate::Error::failed(String::from( - "Only one of the section pointers is pointing to ourself", - ))); + return Err(Error::from_kind( + ErrorKind::OnlyOneOfTheSectionPointersIsPointingToOurself, + )); } // So `other` appears to be a reader for this same struct. No copying is needed. @@ -3766,10 +3832,12 @@ impl<'a> StructBuilder<'a> { wire_helpers::copy_pointer( self.arena, self.segment_id, + #[cfg(feature = "alloc")] self.cap_table, self.pointers.offset(i), other.arena, other.segment_id, + #[cfg(feature = "alloc")] other.cap_table, other.pointers.offset(i), other.nesting_limit, @@ -3785,6 +3853,7 @@ impl<'a> StructBuilder<'a> { #[derive(Clone, Copy)] pub struct ListReader<'a> { arena: &'a dyn ReaderArena, + #[cfg(feature = "alloc")] cap_table: CapTableReader, ptr: *const u8, segment_id: u32, @@ -3801,6 +3870,7 @@ impl<'a> ListReader<'a> { ListReader { arena: &NULL_ARENA, segment_id: 0, + #[cfg(feature = "alloc")] cap_table: CapTableReader::Plain(ptr::null()), ptr: ptr::null(), element_count: 0, @@ -3812,6 +3882,7 @@ impl<'a> ListReader<'a> { } } + #[cfg(feature = "alloc")] pub fn imbue(&mut self, cap_table: CapTableReader) { self.cap_table = cap_table } @@ -3859,6 +3930,7 @@ impl<'a> ListReader<'a> { StructReader { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, data: struct_data, pointers: struct_pointers, @@ -3876,6 +3948,7 @@ impl<'a> ListReader<'a> { PointerReader { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, pointer: unsafe { self.ptr.offset(offset) } as *const _, nesting_limit: self.nesting_limit, @@ -3994,6 +4067,7 @@ impl<'a> ListReader<'a> { pub struct ListBuilder<'a> { arena: &'a mut dyn BuilderArena, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder, ptr: *mut u8, segment_id: u32, @@ -4010,6 +4084,7 @@ impl<'a> ListBuilder<'a> { ListBuilder { arena, segment_id: 0, + #[cfg(feature = "alloc")] cap_table: CapTableBuilder::Plain(ptr::null_mut()), ptr: ptr::null_mut(), element_count: 0, @@ -4024,6 +4099,7 @@ impl<'a> ListBuilder<'a> { ListReader { arena: self.arena.as_reader(), segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table.into_reader(), ptr: self.ptr as *const _, element_count: self.element_count, @@ -4043,6 +4119,7 @@ impl<'a> ListBuilder<'a> { } } + #[cfg(feature = "alloc")] pub fn imbue(&mut self, cap_table: CapTableBuilder) { self.cap_table = cap_table } @@ -4065,6 +4142,7 @@ impl<'a> ListBuilder<'a> { StructBuilder { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, data: struct_data, pointers: struct_pointers, @@ -4083,6 +4161,7 @@ impl<'a> ListBuilder<'a> { PointerBuilder { arena: self.arena, segment_id: self.segment_id, + #[cfg(feature = "alloc")] cap_table: self.cap_table, pointer: unsafe { self.ptr.offset(offset as isize) } as *mut _, } diff --git a/capnp/src/private/read_limiter.rs b/capnp/src/private/read_limiter.rs index 40c6b2880..0bb6a7a84 100644 --- a/capnp/src/private/read_limiter.rs +++ b/capnp/src/private/read_limiter.rs @@ -24,8 +24,7 @@ pub use sync::ReadLimiter; #[cfg(feature = "sync_reader")] mod sync { - use crate::{Error, Result}; - use alloc::string::String; + use crate::{Error, ErrorKind, Result}; use core::sync::atomic::{AtomicUsize, Ordering}; pub struct ReadLimiter { @@ -57,7 +56,7 @@ mod sync { let current = self.limit.load(Ordering::Relaxed); if amount > current && self.error_on_limit_exceeded { - return Err(Error::failed(String::from("read limit exceeded"))); + return Err(Error::from_kind(ErrorKind::ReadLimitExceeded)); } else { // The common case is current >= amount. Note that we only branch once in that case. // If we combined the fields into an Option, we would @@ -75,8 +74,7 @@ pub use unsync::ReadLimiter; #[cfg(not(feature = "sync_reader"))] mod unsync { - use crate::{Error, Result}; - use alloc::string::String; + use crate::{Error, ErrorKind, Result}; use core::cell::Cell; pub struct ReadLimiter { @@ -102,7 +100,7 @@ mod unsync { pub fn can_read(&self, amount: usize) -> Result<()> { let current = self.limit.get(); if amount > current && self.error_on_limit_exceeded { - Err(Error::failed(String::from("read limit exceeded"))) + Err(Error::from_kind(ErrorKind::ReadLimitExceeded)) } else { // The common case is current >= amount. Note that we only branch once in that case. // If we combined the fields into an Option>, we would diff --git a/capnp/src/schema.rs b/capnp/src/schema.rs index 3770c653d..b24f8f4bc 100644 --- a/capnp/src/schema.rs +++ b/capnp/src/schema.rs @@ -1,5 +1,6 @@ //! Convenience wrappers of the datatypes defined in schema.capnp. +#![cfg(feature = "alloc")] use crate::dynamic_value; use crate::introspect::{self, RawBrandedStructSchema, RawEnumSchema}; use crate::private::layout; @@ -70,10 +71,12 @@ impl StructSchema { if let Some(field) = self.find_field_by_name(name)? { Ok(field) } else { - Err(crate::Error::failed(format!( - "field \"{}\" not found", - name - ))) + // error needs to be mutable only when the alloc feature is enabled + #[allow(unused_mut)] + let mut error = crate::Error::from_kind(crate::ErrorKind::FieldNotFound); + #[cfg(feature = "alloc")] + error.extra(name.to_string()); + Err(error) } } diff --git a/capnp/src/serialize.rs b/capnp/src/serialize.rs index b0f390bac..3568201da 100644 --- a/capnp/src/serialize.rs +++ b/capnp/src/serialize.rs @@ -26,19 +26,26 @@ mod no_alloc_slice_segments; pub use no_alloc_slice_segments::NoAllocSliceSegments; +#[cfg(feature = "alloc")] use crate::io::{Read, Write}; -use alloc::string::ToString; +#[cfg(feature = "alloc")] use alloc::vec::Vec; +#[cfg(feature = "alloc")] use core::convert::TryInto; +#[cfg(feature = "alloc")] use core::ops::Deref; use crate::message; +#[cfg(feature = "alloc")] use crate::private::units::BYTES_PER_WORD; -use crate::{Error, Result}; +use crate::Result; +#[cfg(feature = "alloc")] +use crate::{Error, ErrorKind}; pub const SEGMENTS_COUNT_LIMIT: usize = 512; /// Segments read from a single flat slice of words. +#[cfg(feature = "alloc")] pub struct SliceSegments<'a> { words: &'a [u8], @@ -47,6 +54,7 @@ pub struct SliceSegments<'a> { segment_indices: Vec<(usize, usize)>, } +#[cfg(feature = "alloc")] impl<'a> message::ReaderSegments for SliceSegments<'a> { fn get_segment(&self, id: u32) -> Option<&[u8]> { if id < self.segment_indices.len() as u32 { @@ -68,6 +76,7 @@ impl<'a> message::ReaderSegments for SliceSegments<'a> { /// /// ALIGNMENT: If the "unaligned" feature is enabled, then there are no alignment requirements on `slice`. /// Otherwise, `slice` must be 8-byte aligned (attempts to read the message will trigger errors). +#[cfg(feature = "alloc")] pub fn read_message_from_flat_slice<'a>( slice: &mut &'a [u8], options: message::ReaderOptions, @@ -76,17 +85,16 @@ pub fn read_message_from_flat_slice<'a>( let mut bytes = *slice; let orig_bytes_len = bytes.len(); let Some(segment_lengths_builder) = read_segment_table(&mut bytes, options)? else { - return Err(Error::failed("empty slice".to_string())) + return Err(Error::from_kind(ErrorKind::EmptySlice)); }; let segment_table_bytes_len = orig_bytes_len - bytes.len(); assert_eq!(segment_table_bytes_len % BYTES_PER_WORD, 0); let num_words = segment_lengths_builder.total_words(); let body_bytes = &all_bytes[segment_table_bytes_len..]; if num_words > (body_bytes.len() / BYTES_PER_WORD) { - Err(Error::failed(format!( - "Message ends prematurely. Header claimed {} words, but message only has {} words.", + Err(Error::from_kind(ErrorKind::MessageEndsPrematurely( num_words, - body_bytes.len() / BYTES_PER_WORD + body_bytes.len() / BYTES_PER_WORD, ))) } else { *slice = &body_bytes[(num_words * BYTES_PER_WORD)..]; @@ -101,7 +109,7 @@ pub fn read_message_from_flat_slice<'a>( /// The slice is allowed to extend beyond the end of the message. On success, updates `slice` to point /// to the remaining bytes beyond the end of the message. /// -/// Unlike read_message_from_flat_slice_no_alloc it does not do heap allocation (except for error message) +/// Unlike read_message_from_flat_slice_no_alloc it does not do heap allocation /// /// ALIGNMENT: If the "unaligned" feature is enabled, then there are no alignment requirements on `slice`. /// Otherwise, `slice` must be 8-byte aligned (attempts to read the message will trigger errors). @@ -116,6 +124,7 @@ pub fn read_message_from_flat_slice_no_alloc<'a>( /// Segments read from a buffer, useful for when you have the message in a buffer and don't want the extra /// copy of `read_message`. +#[cfg(feature = "alloc")] pub struct BufferSegments { buffer: T, @@ -127,6 +136,7 @@ pub struct BufferSegments { segment_indices: Vec<(usize, usize)>, } +#[cfg(feature = "alloc")] impl> BufferSegments { /// Reads a serialized message (including a segment table) from a buffer and takes ownership, without copying. /// The buffer is allowed to be longer than the message. Provide this to `Reader::new` with options that make @@ -138,7 +148,7 @@ impl> BufferSegments { let mut segment_bytes = &*buffer; let Some(segment_table) = read_segment_table(&mut segment_bytes, options)? else { - return Err(Error::failed("empty buffer".to_string())) + return Err(Error::from_kind(ErrorKind::EmptyBuffer)); }; let segment_table_bytes_len = buffer.len() - segment_bytes.len(); @@ -156,6 +166,7 @@ impl> BufferSegments { } } +#[cfg(feature = "alloc")] impl> message::ReaderSegments for BufferSegments { fn get_segment(&self, id: u32) -> Option<&[u8]> { if id < self.segment_indices.len() as u32 { @@ -176,6 +187,7 @@ impl> message::ReaderSegments for BufferSegments { /// Owned memory containing a message's segments sequentialized in a single contiguous buffer. /// The segments are guaranteed to be 8-byte aligned. +#[cfg(feature = "alloc")] pub struct OwnedSegments { // Each pair represents a segment inside of `owned_space`. // (starting index (in words), ending index (in words)) @@ -184,6 +196,7 @@ pub struct OwnedSegments { owned_space: Vec, } +#[cfg(feature = "alloc")] impl core::ops::Deref for OwnedSegments { type Target = [u8]; fn deref(&self) -> &[u8] { @@ -191,12 +204,14 @@ impl core::ops::Deref for OwnedSegments { } } +#[cfg(feature = "alloc")] impl core::ops::DerefMut for OwnedSegments { fn deref_mut(&mut self) -> &mut [u8] { crate::Word::words_to_bytes_mut(&mut self.owned_space[..]) } } +#[cfg(feature = "alloc")] impl crate::message::ReaderSegments for OwnedSegments { fn get_segment(&self, id: u32) -> Option<&[u8]> { if id < self.segment_indices.len() as u32 { @@ -212,12 +227,14 @@ impl crate::message::ReaderSegments for OwnedSegments { } } +#[cfg(feature = "alloc")] /// Helper object for constructing an `OwnedSegments` or a `SliceSegments`. pub struct SegmentLengthsBuilder { segment_indices: Vec<(usize, usize)>, total_words: usize, } +#[cfg(feature = "alloc")] impl SegmentLengthsBuilder { /// Creates a new `SegmentsLengthsBuilder`, initializing the segment_indices vector with /// `Vec::with_capacitiy(capacity)`. `capacity` should equal the number of times that `push_segment()` @@ -271,6 +288,7 @@ impl SegmentLengthsBuilder { /// Reads a serialized message from a stream with the provided options. /// /// For optimal performance, `read` should be a buffered reader type. +#[cfg(feature = "alloc")] pub fn read_message( mut read: R, options: message::ReaderOptions, @@ -279,7 +297,7 @@ where R: Read, { let Some(owned_segments_builder) = read_segment_table(&mut read, options)? else { - return Err(Error::failed("Premature end of file".to_string())) + return Err(Error::from_kind(ErrorKind::PrematureEndOfFile)); }; read_segments( &mut read, @@ -291,6 +309,7 @@ where /// Like `read_message()`, but returns None instead of an error if there are zero bytes left in /// `read`. This is useful for reading a stream containing an unknown number of messages -- you /// call this function until it returns None. +#[cfg(feature = "alloc")] pub fn try_read_message( mut read: R, options: message::ReaderOptions, @@ -298,7 +317,9 @@ pub fn try_read_message( where R: Read, { - let Some(owned_segments_builder) = read_segment_table(&mut read, options)? else { return Ok(None) }; + let Some(owned_segments_builder) = read_segment_table(&mut read, options)? else { + return Ok(None); + }; Ok(Some(read_segments( &mut read, owned_segments_builder.into_owned_segments(), @@ -311,6 +332,7 @@ where /// /// The segment table format for streams is defined in the Cap'n Proto /// [encoding spec](https://capnproto.org/encoding.html) +#[cfg(feature = "alloc")] fn read_segment_table( read: &mut R, options: message::ReaderOptions, @@ -333,9 +355,13 @@ where let segment_count = u32::from_le_bytes(buf[0..4].try_into().unwrap()).wrapping_add(1) as usize; if segment_count >= SEGMENTS_COUNT_LIMIT { - return Err(Error::failed(format!("Too many segments: {segment_count}"))); + return Err(Error::from_kind(ErrorKind::InvalidNumberOfSegments( + segment_count, + ))); } else if segment_count == 0 { - return Err(Error::failed(format!("Too few segments: {segment_count}"))); + return Err(Error::from_kind(ErrorKind::InvalidNumberOfSegments( + segment_count, + ))); } let mut segment_lengths_builder = SegmentLengthsBuilder::with_capacity(segment_count); @@ -366,10 +392,8 @@ where // size to make the receiver allocate excessive space and possibly crash. if let Some(limit) = options.traversal_limit_in_words { if segment_lengths_builder.total_words() > limit { - return Err(Error::failed(format!( - "Message has {} words, which is too large. To increase the limit on the \ - receiving end, see capnp::message::ReaderOptions.", - segment_lengths_builder.total_words() + return Err(Error::from_kind(ErrorKind::MessageTooLarge( + segment_lengths_builder.total_words(), ))); } } @@ -377,6 +401,7 @@ where Ok(Some(segment_lengths_builder)) } +#[cfg(feature = "alloc")] /// Reads segments from `read`. fn read_segments( read: &mut R, @@ -391,6 +416,7 @@ where } /// Constructs a flat vector containing the entire message, including a segment header. +#[cfg(feature = "alloc")] pub fn write_message_to_words(message: &message::Builder) -> Vec where A: message::Allocator, @@ -400,6 +426,7 @@ where /// Like `write_message_to_words()`, but takes a `ReaderSegments`, allowing it to be /// used on `message::Reader` objects (via `into_segments()`). +#[cfg(feature = "alloc")] pub fn write_message_segments_to_words(message: &R) -> Vec where R: message::ReaderSegments, @@ -407,6 +434,7 @@ where flatten_segments(message) } +#[cfg(feature = "alloc")] fn flatten_segments(segments: &R) -> Vec { let word_count = compute_serialized_size(segments); let segment_count = segments.len(); @@ -431,6 +459,7 @@ fn flatten_segments(segments: &R) -> Vec(mut write: W, message: &message::Builder) -> Result<()> where W: Write, @@ -443,6 +472,7 @@ where /// Like `write_message()`, but takes a `ReaderSegments`, allowing it to be /// used on `message::Reader` objects (via `into_segments()`). +#[cfg(feature = "alloc")] pub fn write_message_segments(mut write: W, segments: &R) -> Result<()> where W: Write, @@ -452,6 +482,7 @@ where write_segments(&mut write, segments) } +#[cfg(feature = "alloc")] fn write_segment_table(write: &mut W, segments: &[&[u8]]) -> Result<()> where W: Write, @@ -462,6 +493,7 @@ where /// Writes a segment table to `write`. /// /// `segments` must contain at least one segment. +#[cfg(feature = "alloc")] fn write_segment_table_internal(write: &mut W, segments: &R) -> Result<()> where W: Write, @@ -512,6 +544,7 @@ where } /// Writes segments to `write`. +#[cfg(feature = "alloc")] fn write_segments(write: &mut W, segments: &R) -> Result<()> where W: Write, @@ -526,6 +559,7 @@ where Ok(()) } +#[cfg(feature = "alloc")] fn compute_serialized_size(segments: &R) -> usize { // Table size let len = segments.len(); @@ -542,6 +576,7 @@ fn compute_serialized_size(segments: &R) -> /// /// Multiply this by 8 (or `std::mem::size_of::()`) to get the number of bytes /// that [`write_message()`](fn.write_message.html) will write. +#[cfg(feature = "alloc")] pub fn compute_serialized_size_in_words(message: &crate::message::Builder) -> usize where A: crate::message::Allocator, @@ -549,6 +584,7 @@ where compute_serialized_size(&message.get_segments_for_output()) } +#[cfg(feature = "alloc")] #[cfg(test)] pub mod test { use alloc::vec::Vec; diff --git a/capnp/src/serialize/no_alloc_slice_segments.rs b/capnp/src/serialize/no_alloc_slice_segments.rs index 7ce40a2b4..c777485b7 100644 --- a/capnp/src/serialize/no_alloc_slice_segments.rs +++ b/capnp/src/serialize/no_alloc_slice_segments.rs @@ -1,12 +1,9 @@ use core::convert::TryInto; -use alloc::string::ToString; - use crate::message::ReaderOptions; use crate::message::ReaderSegments; use crate::private::units::BYTES_PER_WORD; -use crate::Error; -use crate::Result; +use crate::{Error, ErrorKind, Result}; use super::SEGMENTS_COUNT_LIMIT; @@ -46,8 +43,8 @@ impl<'b> NoAllocSliceSegments<'b> { let segments_count = u32_to_segments_count(read_u32_le(&mut remaining)?)?; if segments_count >= SEGMENTS_COUNT_LIMIT { - return Err(Error::failed(format!( - "Too many segments: {segments_count}" + return Err(Error::from_kind(ErrorKind::InvalidNumberOfSegments( + segments_count, ))); } @@ -59,12 +56,7 @@ impl<'b> NoAllocSliceSegments<'b> { total_segments_length_bytes = total_segments_length_bytes .checked_add(segment_length_in_bytes) - .ok_or_else(|| { - Error::failed( - "Message is too large; it's size cannot be represented in usize." - .to_string(), - ) - })?; + .ok_or_else(|| Error::from_kind(ErrorKind::MessageSizeOverflow))?; } // Don't accept a message which the receiver couldn't possibly traverse without hitting the @@ -73,9 +65,8 @@ impl<'b> NoAllocSliceSegments<'b> { if let Some(limit) = options.traversal_limit_in_words { let total_segments_length_words = total_segments_length_bytes / 8; if total_segments_length_words > limit { - return Err(Error::failed(format!( - "Message has {total_segments_length_words} words, which is too large. To increase the limit on the \ - receiving end, see capnp::message::ReaderOptions." + return Err(Error::from_kind(ErrorKind::MessageTooLarge( + total_segments_length_words, ))); } } @@ -86,11 +77,8 @@ impl<'b> NoAllocSliceSegments<'b> { let _padding = read_u32_le(&mut remaining)?; } - let expected_data_offset = calculate_data_offset(segments_count).ok_or_else(|| { - Error::failed( - "Message is too large; it's size cannot be represented in usize".to_string(), - ) - })?; + let expected_data_offset = calculate_data_offset(segments_count) + .ok_or_else(|| Error::from_kind(ErrorKind::MessageSizeOverflow))?; let consumed_bytes = slice.len() - remaining.len(); @@ -103,10 +91,9 @@ impl<'b> NoAllocSliceSegments<'b> { // is malformed. It looks like it's ok to have extra bytes in the end, according to // of `SliceSegments` implementation. if remaining.len() < total_segments_length_bytes { - return Err(Error::failed(format!( - "Message ends prematurely. Header claimed {} words, but message only has {} words.", + return Err(Error::from_kind(ErrorKind::MessageEndsPrematurely( total_segments_length_bytes / BYTES_PER_WORD, - remaining.len() / BYTES_PER_WORD + remaining.len() / BYTES_PER_WORD, ))); } @@ -196,11 +183,8 @@ fn verify_alignment(ptr: *const u8) -> Result<()> { if ptr.align_offset(BYTES_PER_WORD) == 0 { Ok(()) } else { - Err(Error::failed( - "Message was not aligned by 8 bytes boundary. \ - Either ensure that message is properly aligned or compile \ - `capnp` crate with \"unaligned\" feature enabled." - .to_string(), + Err(Error::from_kind( + ErrorKind::MessageNotAlignedBy8BytesBoundary, )) } } @@ -209,7 +193,10 @@ fn verify_alignment(ptr: *const u8) -> Result<()> { /// Returns Error if there are not enough bytes to read u32 fn read_u32_le(slice: &mut &[u8]) -> Result { if slice.len() < U32_LEN_IN_BYTES { - return Err(Error::disconnected("Message ended too soon".to_string())); + return Err(Error::from_kind(ErrorKind::MessageEndsPrematurely( + U32_LEN_IN_BYTES, + slice.len(), + ))); } // Panic safety: we just confirmed that `slice` has at least `U32_LEN_IN_BYTES` so nothing @@ -230,13 +217,7 @@ fn u32_to_segments_count(val: u32) -> Result { // We need to do +1 to value read from the stream. let result = result.and_then(|v: usize| v.checked_add(1)); - result.ok_or_else(|| { - Error::failed( - "Cannot represent 4 byte length as `usize`. This may indicate that you are \ - running on 8 or 16 bit platform or message is too large." - .to_string(), - ) - }) + result.ok_or_else(|| Error::from_kind(ErrorKind::FourByteLengthTooBigForUSize)) } /// Converts 32 bit vlaue which represents encoded segment length to usize segment length in bytes @@ -246,13 +227,7 @@ fn u32_to_segment_length_bytes(val: u32) -> Result { let length_in_bytes = length_in_words.and_then(|l| l.checked_mul(BYTES_PER_WORD)); - length_in_bytes.ok_or_else(|| { - Error::failed( - "Cannot represent 4 byte segment length as usize. This may indicate that \ - you are running on 8 or 16 bit platform or segment is too large" - .to_string(), - ) - }) + length_in_bytes.ok_or_else(|| Error::from_kind(ErrorKind::FourByteSegmentLengthTooBigForUSize)) } /// Calculates expected offset of the message data (beginning of first segment) @@ -314,19 +289,26 @@ fn calculate_data_offset(segments_count: usize) -> Option { #[cfg(test)] mod tests { + #[cfg(feature = "alloc")] use quickcheck::{quickcheck, TestResult}; + use crate::serialize::no_alloc_slice_segments::calculate_data_offset; + #[cfg(feature = "alloc")] use crate::{ message::{ReaderOptions, ReaderSegments}, - serialize::{self, no_alloc_slice_segments::calculate_data_offset}, - word, OutputSegments, Word, + serialize, word, Word, }; + #[cfg(feature = "alloc")] + use crate::OutputSegments; + + #[cfg(feature = "alloc")] + use super::NoAllocSliceSegments; use super::{ read_u32_le, u32_to_segment_length_bytes, u32_to_segments_count, verify_alignment, - NoAllocSliceSegments, }; + #[cfg(feature = "alloc")] use alloc::vec::Vec; #[repr(align(8))] @@ -407,6 +389,7 @@ mod tests { assert_eq!(calculate_data_offset(101), Some(408)); } + #[cfg(feature = "alloc")] quickcheck! { #[cfg_attr(miri, ignore)] // miri takes a long time with quickcheck fn test_no_alloc_buffer_segments_single_segment_optimization( @@ -462,6 +445,7 @@ mod tests { } } + #[cfg(feature = "alloc")] #[test] fn test_no_alloc_buffer_segments_message_postfix() { let output_segments = OutputSegments::SingleSegment([&[1, 2, 3, 4, 5, 6, 7, 8]]); @@ -477,6 +461,7 @@ mod tests { assert_eq!(*remaining, &[11, 12, 13, 14, 15, 16, 0, 0]); } + #[cfg(feature = "alloc")] #[test] fn test_no_alloc_buffer_segments_message_invalid() { let mut buf = vec![]; @@ -500,6 +485,7 @@ mod tests { buf.clear(); } + #[cfg(feature = "alloc")] quickcheck! { #[cfg_attr(miri, ignore)] // miri takes a long time with quickcheck fn test_no_alloc_buffer_segments_message_truncated(segments_vec: Vec>) -> TestResult { diff --git a/capnp/src/serialize_packed.rs b/capnp/src/serialize_packed.rs index e36ef3945..286a36950 100644 --- a/capnp/src/serialize_packed.rs +++ b/capnp/src/serialize_packed.rs @@ -23,12 +23,13 @@ //! [packed stream encoding](https://capnproto.org/encoding.html#packing). use crate::io::{BufRead, Read, Write}; -use alloc::string::ToString; use core::{mem, ptr, slice}; +#[cfg(feature = "alloc")] use crate::message; +#[cfg(feature = "alloc")] use crate::serialize; -use crate::Result; +use crate::{Error, ErrorKind, Result}; /// A `BufRead` wrapper that unpacks packed data. Returns an error on any `read()` /// call that would end within an all-zero (tag 0x00) or uncompressed (tag 0xff) @@ -68,7 +69,7 @@ macro_rules! refresh_buffer( $size = ptr_sub($in_end, $in_ptr); $buffer_begin = b; if $size == 0 { - return Err(crate::Error::failed("Premature end of packed input.".to_string())); + return Err(Error::from_kind(ErrorKind::PrematureEndOfPackedInput)); } } ); @@ -165,8 +166,8 @@ where in_ptr = in_ptr.offset(1); if run_length > ptr_sub(out_end, out) { - return Err(crate::Error::failed( - "Packed input did not end cleanly on a segment boundary.".to_string(), + return Err(Error::from_kind( + ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary, )); } @@ -182,8 +183,8 @@ where in_ptr = in_ptr.offset(1); if run_length > ptr_sub(out_end, out) { - return Err(crate::Error::failed( - "Packed input did not end cleanly on a segment boundary.".to_string(), + return Err(Error::from_kind( + ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary, )); } @@ -230,6 +231,7 @@ where } /// Reads a packed message from a stream using the provided options. +#[cfg(feature = "alloc")] pub fn read_message( read: R, options: message::ReaderOptions, @@ -242,6 +244,7 @@ where } /// Like read_message(), but returns None instead of an error if there are zero bytes left in `read`. +#[cfg(feature = "alloc")] pub fn try_read_message( read: R, options: message::ReaderOptions, @@ -405,6 +408,7 @@ where /// /// The only source of errors from this function are `write.write_all()` calls. If you pass in /// a writer that never returns an error, then this function will never return an error. +#[cfg(feature = "alloc")] pub fn write_message(write: W, message: &crate::message::Builder) -> Result<()> where W: Write, @@ -414,9 +418,9 @@ where serialize::write_message(packed_write, message) } +#[cfg(feature = "alloc")] #[cfg(test)] mod tests { - use alloc::string::ToString; use alloc::vec::Vec; use crate::io::{Read, Write}; @@ -427,6 +431,7 @@ mod tests { use crate::message::ReaderOptions; use crate::serialize::test::write_message_segments; use crate::serialize_packed::{PackedRead, PackedWrite}; + use crate::ErrorKind; #[test] pub fn premature_eof() { @@ -564,8 +569,8 @@ mod tests { Ok(_) => panic!("should have been an error"), Err(e) => { assert_eq!( - e.to_string(), - "Failed: Packed input did not end cleanly on a segment boundary." + e.kind, + ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary, ); } } @@ -580,7 +585,7 @@ mod tests { match packed_read.read_exact(&mut bytes[..]) { Ok(_) => panic!("should have been an error"), Err(e) => { - assert_eq!(e.to_string(), "Failed: Premature end of packed input."); + assert_eq!(e.kind, ErrorKind::PrematureEndOfPackedInput); } } } diff --git a/capnp/src/stringify.rs b/capnp/src/stringify.rs index a7bb8a7a4..253eaa7fd 100644 --- a/capnp/src/stringify.rs +++ b/capnp/src/stringify.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "alloc")] use crate::dynamic_value; use core::fmt::{self, Formatter}; diff --git a/capnp/src/struct_list.rs b/capnp/src/struct_list.rs index 7c8103e23..dc9fb43a5 100644 --- a/capnp/src/struct_list.rs +++ b/capnp/src/struct_list.rs @@ -284,6 +284,7 @@ where } } +#[cfg(feature = "alloc")] impl<'a, T: crate::traits::OwnedStruct> From> for crate::dynamic_value::Reader<'a> { fn from(t: Reader<'a, T>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::List(crate::dynamic_list::Reader::new( @@ -293,6 +294,7 @@ impl<'a, T: crate::traits::OwnedStruct> From> for crate::dynamic_v } } +#[cfg(feature = "alloc")] impl<'a, T: crate::traits::OwnedStruct> From> for crate::dynamic_value::Builder<'a> { fn from(t: Builder<'a, T>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::List(crate::dynamic_list::Builder::new( diff --git a/capnp/src/text.rs b/capnp/src/text.rs index 337f22bb4..f1184a582 100644 --- a/capnp/src/text.rs +++ b/capnp/src/text.rs @@ -23,7 +23,7 @@ use core::{convert, ops, str}; -use crate::{Error, Result}; +use crate::{Error, ErrorKind, Result}; #[derive(Copy, Clone)] pub struct Owned(()); @@ -44,7 +44,7 @@ pub type Reader<'a> = &'a str; pub fn new_reader(v: &[u8]) -> Result> { match str::from_utf8(v) { Ok(v) => Ok(v), - Err(e) => Err(Error::failed(format!("Text contains non-utf8 data: {e:?}"))), + Err(e) => Err(Error::from_kind(ErrorKind::TextContainsNonUtf8Data(e))), } } @@ -66,7 +66,7 @@ impl<'a> Builder<'a> { pub fn new(bytes: &mut [u8], pos: u32) -> Result> { if pos != 0 { if let Err(e) = str::from_utf8(bytes) { - return Err(Error::failed(format!("Text contains non-utf8 data: {e:?}"))); + return Err(Error::from_kind(ErrorKind::TextContainsNonUtf8Data(e))); } } Ok(Builder { @@ -151,12 +151,14 @@ impl<'a> crate::traits::SetPointerBuilder for Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Reader<'a> { fn from(t: Reader<'a>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::Text(t) } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Builder<'a> { fn from(t: Builder<'a>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::Text(t) diff --git a/capnp/src/text_list.rs b/capnp/src/text_list.rs index 72cbd2647..873d73b9d 100644 --- a/capnp/src/text_list.rs +++ b/capnp/src/text_list.rs @@ -197,6 +197,7 @@ impl<'a> ::core::iter::IntoIterator for Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Reader<'a> { fn from(t: Reader<'a>) -> crate::dynamic_value::Reader<'a> { crate::dynamic_value::Reader::List(crate::dynamic_list::Reader { @@ -206,6 +207,7 @@ impl<'a> From> for crate::dynamic_value::Reader<'a> { } } +#[cfg(feature = "alloc")] impl<'a> From> for crate::dynamic_value::Builder<'a> { fn from(t: Builder<'a>) -> crate::dynamic_value::Builder<'a> { crate::dynamic_value::Builder::List(crate::dynamic_list::Builder { diff --git a/capnp/src/traits.rs b/capnp/src/traits.rs index 1ca28ce19..5b0be701a 100644 --- a/capnp/src/traits.rs +++ b/capnp/src/traits.rs @@ -19,8 +19,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#[cfg(feature = "alloc")] +use crate::private::layout::CapTable; use crate::private::layout::{ - CapTable, ListReader, PointerBuilder, PointerReader, StructBuilder, StructReader, StructSize, + ListReader, PointerBuilder, PointerReader, StructBuilder, StructReader, StructSize, }; use crate::Result; @@ -87,10 +89,12 @@ pub trait SetPointerBuilder { ) -> Result<()>; } +#[cfg(feature = "alloc")] pub trait Imbue<'a> { fn imbue(&mut self, caps: &'a CapTable); } +#[cfg(feature = "alloc")] pub trait ImbueMut<'a> { fn imbue_mut(&mut self, caps: &'a mut CapTable); } diff --git a/capnp/tests/canonicalize.rs b/capnp/tests/canonicalize.rs index c0404ad44..43631a63f 100644 --- a/capnp/tests/canonicalize.rs +++ b/capnp/tests/canonicalize.rs @@ -21,6 +21,7 @@ use capnp::message; +#[cfg(feature = "alloc")] #[test] fn canonicalize_succeeds_on_null_message() { let segment: &[capnp::Word] = &[capnp::word(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)]; @@ -34,6 +35,7 @@ fn canonicalize_succeeds_on_null_message() { assert_eq!(&canonical_bytes[..], segment); } +#[cfg(feature = "alloc")] #[test] fn dont_truncate_struct_too_far() { let segment: &[capnp::Word] = &[ @@ -63,6 +65,7 @@ fn dont_truncate_struct_too_far() { assert_eq!(&canonicalized[..], canonical_segment); } +#[cfg(feature = "alloc")] #[test] fn dont_truncate_struct_list_too_far() { let segment: &[capnp::Word] = &[ @@ -175,6 +178,7 @@ fn is_canonical_requires_dense_packing() { assert!(!message.is_canonical().unwrap()); } +#[cfg(feature = "alloc")] #[test] fn simple_multisegment_message() { let segment0: &[capnp::Word] = &[ @@ -206,6 +210,7 @@ fn simple_multisegment_message() { assert_eq!(&canonicalized[..], canonical_segment); } +#[cfg(feature = "alloc")] #[test] fn multisegment_only_first_segment_used() { // A segment with a canonicalized struct. @@ -265,6 +270,7 @@ fn is_canonical_rejects_unused_trailing_words() { assert!(!message.is_canonical().unwrap()); } +#[cfg(feature = "alloc")] #[test] fn empty_inline_composite_list_of_0_sized_structs() { let segment: &[capnp::Word] = &[ @@ -285,6 +291,7 @@ fn empty_inline_composite_list_of_0_sized_structs() { assert_eq!(segment, &canonical_words[..]); } +#[cfg(feature = "alloc")] #[test] fn inline_composite_list_with_void_list() { let segment: &[capnp::Word] = &[ @@ -330,6 +337,7 @@ fn is_canonical_rejects_inline_composite_list_with_inaccurate_word_length() { assert!(!message.is_canonical().unwrap()); } +#[cfg(feature = "alloc")] #[test] fn truncate_data_section_inline_composite() { let segment: &[capnp::Word] = &[ @@ -354,6 +362,7 @@ fn truncate_data_section_inline_composite() { assert!(canonical_message.is_canonical().unwrap()); } +#[cfg(feature = "alloc")] #[test] fn truncate_pointer_section_inline_composite() { let segment: &[capnp::Word] = &[ @@ -387,6 +396,7 @@ fn truncate_pointer_section_inline_composite() { assert_eq!(expected_canonical_words, &canonical_words[..]); } +#[cfg(feature = "alloc")] #[test] fn list_padding_must_be_zero() { let segment: &[capnp::Word] = &[ @@ -416,6 +426,7 @@ fn list_padding_must_be_zero() { assert_eq!(expected_canonical_words, &canonical_words[..]); } +#[cfg(feature = "alloc")] #[test] fn bit_list_padding_must_be_zero() { let segment: &[capnp::Word] = &[ diff --git a/capnpc/src/codegen.rs b/capnpc/src/codegen.rs index 73a98c9e0..a3fdd4295 100644 --- a/capnpc/src/codegen.rs +++ b/capnpc/src/codegen.rs @@ -302,7 +302,9 @@ impl<'a> GeneratorContext<'a> { node_id: u64, ) -> ::capnp::Result<()> { // unused nodes in imported files might be omitted from the node map - let Some(&node_reader) = self.node_map.get(&node_id) else { return Ok(()) }; + let Some(&node_reader) = self.node_map.get(&node_id) else { + return Ok(()); + }; for annotation in node_reader.get_annotations()? { if annotation.get_id() == NAME_ANNOTATION_ID { @@ -1328,7 +1330,9 @@ fn used_params_of_brand( let brand_scopes = brand_scopes; // freeze let mut current_node_id = node_id; loop { - let Some(current_node) = ctx.node_map.get(¤t_node_id) else { break }; + let Some(current_node) = ctx.node_map.get(¤t_node_id) else { + break; + }; let params = current_node.get_parameters()?; match brand_scopes.get(¤t_node_id) { None => (), diff --git a/capnpc/src/lib.rs b/capnpc/src/lib.rs index fbe7fb592..8280b0340 100644 --- a/capnpc/src/lib.rs +++ b/capnpc/src/lib.rs @@ -90,7 +90,7 @@ pub(crate) fn convert_io_err(err: std::io::Error) -> capnp::Error { _ => capnp::ErrorKind::Failed, }; capnp::Error { - description: format!("{err}"), + extra: format!("{err}"), kind, } } @@ -399,6 +399,6 @@ impl CompilerCommand { #[cfg_attr(miri, ignore)] fn compiler_command_new_no_out_dir() { std::env::remove_var("OUT_DIR"); - let error = CompilerCommand::new().run().unwrap_err().description; + let error = CompilerCommand::new().run().unwrap_err().extra; assert!(error.starts_with("Could not access `OUT_DIR` environment variable")); } diff --git a/capnpc/test/test.rs b/capnpc/test/test.rs index aac62558a..3b0fbbc38 100644 --- a/capnpc/test/test.rs +++ b/capnpc/test/test.rs @@ -114,7 +114,11 @@ mod tests { schema::StructSchema, }; - let TypeVariant::Struct(schema) = field_subset_indexes_correctly::Owned::introspect().which() else { unreachable!() }; + let TypeVariant::Struct(schema) = + field_subset_indexes_correctly::Owned::introspect().which() + else { + unreachable!() + }; let schema = StructSchema::new(schema); let subset = schema.get_non_union_fields().unwrap(); @@ -1590,7 +1594,7 @@ mod tests { match message.get_root::>() { Ok(_) => panic!("expected out-of-bounds error"), Err(e) => { - assert_eq!(e.description, "message contained out-of-bounds pointer") + assert_eq!(&e.to_string(), "Message contains out-of-bounds pointer") } } } @@ -1623,7 +1627,7 @@ mod tests { match message.get_root::>() { Ok(_) => panic!("expected out-of-bounds error"), Err(e) => { - assert_eq!(e.description, "message contained out-of-bounds pointer") + assert_eq!(e.to_string(), "Message contains out-of-bounds pointer") } } } @@ -1688,7 +1692,7 @@ mod tests { match root.total_size() { Err(e) => assert_eq!( "InlineComposite list's elements overrun its word count.", - e.description + &e.to_string() ), _ => panic!("did not get expected error"), } @@ -1706,7 +1710,7 @@ mod tests { match builder_root.get_any_pointer_field().set_as(root) { Err(e) => assert_eq!( "InlineComposite list's elements overrun its word count.", - e.description + &e.to_string() ), _ => panic!("did not get expected error"), } @@ -1829,7 +1833,7 @@ mod tests { *ReaderOptions::new().traversal_limit_in_words(Some(2)), ); match reader.get_root::>() { - Err(e) => assert_eq!(e.description, "read limit exceeded"), + Err(e) => assert_eq!(&e.to_string(), "Read limit exceeded"), Ok(_) => panic!("expected error"), } }