Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,19 @@ struct Deserializer<R> {
max_remaining_depth: usize,
}

impl<'de, R: Read> Deserializer<TeeReader<'de, R>> {
fn from_reader(input: &'de mut R, max_remaining_depth: usize) -> Self {
impl<R: Read> Deserializer<TeeReader<R>> {
fn from_reader(input: R, max_remaining_depth: usize) -> Self {
Deserializer {
input: TeeReader::new(input),
max_remaining_depth,
}
}
}

impl<'de> Deserializer<&'de [u8]> {
impl<R> Deserializer<R> {
/// Creates a new `Deserializer` which will be deserializing the provided
/// input.
fn new(input: &'de [u8], max_remaining_depth: usize) -> Self {
fn new(input: R, max_remaining_depth: usize) -> Self {
Comment on lines +157 to +160

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not something that we really need to deal with right now but it would be interesting if we could restructure this crate to be able to operate in no_std environments since afaik the only usage of std we really have today is via the std::io traits

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone submitted this PR: #15

Deserializer {
input,
max_remaining_depth,
Expand All @@ -166,24 +166,24 @@ impl<'de> Deserializer<&'de [u8]> {
}

/// A reader that can optionally capture all bytes from an underlying [`Read`]er
struct TeeReader<'de, R> {
struct TeeReader<R> {
/// the underlying reader
reader: &'de mut R,
reader: R,
/// If non-empty, all bytes read from the underlying reader will be captured in the last entry here.
captured_keys: Vec<Vec<u8>>,
}

impl<'de, R> TeeReader<'de, R> {
impl<R> TeeReader<R> {
/// Wraps the provided reader in a new [`TeeReader`].
pub fn new(reader: &'de mut R) -> Self {
pub fn new(reader: R) -> Self {
Self {
reader,
captured_keys: Vec::new(),
}
}
}

impl<'de, R: Read> Read for TeeReader<'de, R> {
impl<R: Read> Read for TeeReader<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let bytes_read = self.reader.read(buf)?;
if let Some(buffer) = self.captured_keys.last_mut() {
Expand Down Expand Up @@ -291,7 +291,7 @@ trait BcsDeserializer<'de> {
}
}

impl<'de, R: Read> Deserializer<TeeReader<'de, R>> {
impl<R: Read> Deserializer<TeeReader<R>> {
fn parse_vec(&mut self) -> Result<Vec<u8>> {
let len = self.parse_length()?;
let mut output = vec![0; len];
Expand All @@ -305,7 +305,7 @@ impl<'de, R: Read> Deserializer<TeeReader<'de, R>> {
}
}

impl<'de, R: Read> BcsDeserializer<'de> for Deserializer<TeeReader<'de, R>> {
impl<'de, R: Read> BcsDeserializer<'de> for Deserializer<TeeReader<R>> {
type MaybeBorrowedBytes = Vec<u8>;

fn fill_slice(&mut self, slice: &mut [u8]) -> Result<()> {
Expand Down Expand Up @@ -430,7 +430,7 @@ impl<R> Deserializer<R> {
}
}

impl<'de, 'a, R> de::Deserializer<'de> for &'a mut Deserializer<R>
impl<'de, R> de::Deserializer<'de> for &mut Deserializer<R>
where
Deserializer<R>: BcsDeserializer<'de>,
{
Expand Down Expand Up @@ -788,7 +788,7 @@ where
}
}

impl<'de, 'a, R> de::EnumAccess<'de> for &'a mut Deserializer<R>
impl<'de, R> de::EnumAccess<'de> for &mut Deserializer<R>
where
Deserializer<R>: BcsDeserializer<'de>,
{
Expand All @@ -805,7 +805,7 @@ where
}
}

impl<'de, 'a, R> de::VariantAccess<'de> for &'a mut Deserializer<R>
impl<'de, R> de::VariantAccess<'de> for &mut Deserializer<R>
where
Deserializer<R>: BcsDeserializer<'de>,
{
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! * provide good performance and concise (binary) representations;
//! * support a rich set of data types commonly used in Rust;
//! * enforce canonical serialization, meaning that every value of a given type should have
//! a single valid representation.
//! a single valid representation.
//!
//! BCS also aims to mitigate the consequence of malicious inputs by enforcing well-defined limits
//! on large or nested containers during (de)serialization.
Expand Down Expand Up @@ -44,7 +44,7 @@
//! applications must carefully plan in advance for adhoc extension points:
//! * Enums may be used for explicit versioning and backward compatibility (e.g. extensible query interfaces).
//! * In some cases, data fields of type `Vec<u8>` may also be added to allow (future) unknown payloads
//! in serialized form.
//! in serialized form.
//!
//! ## Detailed Specifications
//!
Expand Down
78 changes: 40 additions & 38 deletions src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,28 @@ where
}

/// Same as `to_bytes` but write directly into an `std::io::Write` object.
pub fn serialize_into<W, T>(write: &mut W, value: &T) -> Result<()>
pub fn serialize_into<T>(mut write: impl std::io::Write, value: &T) -> Result<()>
where
W: ?Sized + std::io::Write,
T: ?Sized + Serialize,
Comment on lines +73 to 75

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think losing the W: ?Sized bound is a real functional loss right? trait objects would no longer be usable via this api.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm good question. Let me check

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no loss of functionality: &mut W was always sized even when W was not Sized.

{
let serializer = Serializer::new(write, crate::MAX_CONTAINER_DEPTH);
let serializer = Serializer::new(&mut write, crate::MAX_CONTAINER_DEPTH);
value.serialize(serializer)
}

/// Same as `serialize_into` but use `limit` as max container depth instead of MAX_CONTAINER_DEPTH
/// Note that `limit` has to be lower than MAX_CONTAINER_DEPTH
pub fn serialize_into_with_limit<W, T>(write: &mut W, value: &T, limit: usize) -> Result<()>
pub fn serialize_into_with_limit<T>(
mut write: impl std::io::Write,
Comment on lines +83 to +84

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i would probably keep the generic bounds but we could have this just be W instead of &mut W

@ma2bd ma2bd Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing: We're basically replacing &mut (impl std::io::Write + ?Sized) by impl std::io::Write which is more general.

value: &T,
limit: usize,
) -> Result<()>
where
W: ?Sized + std::io::Write,
T: ?Sized + Serialize,
{
if limit > crate::MAX_CONTAINER_DEPTH {
return Err(Error::NotSupported("limit exceeds the max allowed depth"));
}
let serializer = Serializer::new(write, limit);
let serializer = Serializer::new(&mut write, limit);
value.serialize(serializer)
}

Expand Down Expand Up @@ -140,17 +142,17 @@ pub fn is_human_readable() -> bool {
}

/// Serialization implementation for BCS
struct Serializer<'a, W: ?Sized> {
output: &'a mut W,
struct Serializer<W> {
output: W,
max_remaining_depth: usize,
}

impl<'a, W> Serializer<'a, W>
impl<W> Serializer<W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
/// Creates a new `Serializer` which will emit BCS.
fn new(output: &'a mut W, max_remaining_depth: usize) -> Self {
fn new(output: W, max_remaining_depth: usize) -> Self {
Self {
output,
max_remaining_depth,
Expand Down Expand Up @@ -190,17 +192,17 @@ where
}
}

impl<'a, W> ser::Serializer for Serializer<'a, W>
impl<'a, W> ser::Serializer for Serializer<&'a mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = MapSerializer<'a, W>;
type SerializeMap = MapSerializer<&'a mut W>;
type SerializeStruct = Self;
type SerializeStructVariant = Self;

Expand Down Expand Up @@ -403,9 +405,9 @@ where
}
}

impl<'a, W> ser::SerializeSeq for Serializer<'a, W>
impl<W> ser::SerializeSeq for Serializer<&mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand All @@ -414,17 +416,17 @@ where
where
T: ?Sized + Serialize,
{
value.serialize(Serializer::new(self.output, self.max_remaining_depth))
value.serialize(Serializer::new(&mut *self.output, self.max_remaining_depth))
}

fn end(self) -> Result<()> {
Ok(())
}
}

impl<'a, W> ser::SerializeTuple for Serializer<'a, W>
impl<W> ser::SerializeTuple for Serializer<&mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand All @@ -433,17 +435,17 @@ where
where
T: ?Sized + Serialize,
{
value.serialize(Serializer::new(self.output, self.max_remaining_depth))
value.serialize(Serializer::new(&mut *self.output, self.max_remaining_depth))
}

fn end(self) -> Result<()> {
Ok(())
}
}

impl<'a, W> ser::SerializeTupleStruct for Serializer<'a, W>
impl<W> ser::SerializeTupleStruct for Serializer<&mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand All @@ -452,17 +454,17 @@ where
where
T: ?Sized + Serialize,
{
value.serialize(Serializer::new(self.output, self.max_remaining_depth))
value.serialize(Serializer::new(&mut *self.output, self.max_remaining_depth))
}

fn end(self) -> Result<()> {
Ok(())
}
}

impl<'a, W> ser::SerializeTupleVariant for Serializer<'a, W>
impl<W> ser::SerializeTupleVariant for Serializer<&mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand All @@ -471,7 +473,7 @@ where
where
T: ?Sized + Serialize,
{
value.serialize(Serializer::new(self.output, self.max_remaining_depth))
value.serialize(Serializer::new(&mut *self.output, self.max_remaining_depth))
}

fn end(self) -> Result<()> {
Expand All @@ -480,14 +482,14 @@ where
}

#[doc(hidden)]
struct MapSerializer<'a, W: ?Sized> {
serializer: Serializer<'a, W>,
pub struct MapSerializer<W> {
serializer: Serializer<W>,
entries: Vec<(Vec<u8>, Vec<u8>)>,
next_key: Option<Vec<u8>>,
}

impl<'a, W: ?Sized> MapSerializer<'a, W> {
fn new(serializer: Serializer<'a, W>) -> Self {
impl<W> MapSerializer<W> {
fn new(serializer: Serializer<W>) -> Self {
MapSerializer {
serializer,
entries: Vec::new(),
Expand All @@ -496,9 +498,9 @@ impl<'a, W: ?Sized> MapSerializer<'a, W> {
}
}

impl<'a, W> ser::SerializeMap for MapSerializer<'a, W>
impl<W> ser::SerializeMap for MapSerializer<W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand Down Expand Up @@ -557,9 +559,9 @@ where
}
}

impl<'a, W> ser::SerializeStruct for Serializer<'a, W>
impl<W> ser::SerializeStruct for Serializer<&mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand All @@ -568,17 +570,17 @@ where
where
T: ?Sized + Serialize,
{
value.serialize(Serializer::new(self.output, self.max_remaining_depth))
value.serialize(Serializer::new(&mut *self.output, self.max_remaining_depth))
}

fn end(self) -> Result<()> {
Ok(())
}
}

impl<'a, W> ser::SerializeStructVariant for Serializer<'a, W>
impl<W> ser::SerializeStructVariant for Serializer<&mut W>
where
W: ?Sized + std::io::Write,
W: std::io::Write,
{
type Ok = ();
type Error = Error;
Expand All @@ -587,7 +589,7 @@ where
where
T: ?Sized + Serialize,
{
value.serialize(Serializer::new(self.output, self.max_remaining_depth))
value.serialize(Serializer::new(&mut *self.output, self.max_remaining_depth))
}

fn end(self) -> Result<()> {
Expand Down
Loading