#no-alloc #framework #serialization #no-std

no-std encode

A simple framework for encoding binary data

9 releases

new 0.2.4 Apr 19, 2025
0.2.3 Apr 19, 2025
0.2.1 Mar 26, 2025
0.1.3 Mar 15, 2025
0.1.2 Feb 16, 2025

#473 in Encoding

Download history 317/week @ 2025-02-15 29/week @ 2025-02-22 13/week @ 2025-03-01 4/week @ 2025-03-08 255/week @ 2025-03-15 153/week @ 2025-03-22 46/week @ 2025-03-29 12/week @ 2025-04-05 118/week @ 2025-04-12

332 downloads per month

MIT license

79KB
2K SLoC

Encode, a Rust library for building encoders and serializers

LICENSE CI codecov Crates.io Version

Encoders/serializers made easy.

encode is a toolbox for building encoders and serializers in Rust. It is heavily inspired by the winnow and nom crates, which are used for building parsers. It is meant to be a companion to these crates, providing a similar level of flexibility and ease of use for reversing the parsing process.

The main idea behind encode is to provide a set of combinators for building serializers. These combinators can be used to build complex encoders from simple building blocks. This makes it easy to build encoders for different types of data, without having to write a lot of boilerplate code.

Another key feature of encode is its support for no_std environments. This makes it suitable for use in embedded systems, where the standard library (and particularly the std::io module) is not available.

See the examples folder for some examples of how to use encode. Also, check the combinators module for a list of all the combinators provided by the crate.

Feature highlights

  • #![no_std] compatible
  • #![forbid(unsafe_code)]
  • Simple and flexible API
  • Minimal dependencies
  • Ready to use combinators for minimizing boilerplate.
  • Write encoders that serialize data to UTF-8 and/or raw bytes

Cargo features

FAQs

Why the ByteEncoder trait instead of bytes::BufMut?

From bytes documentation

A buffer stores bytes in memory such that write operations are infallible. The underlying storage may or may not be in contiguous memory. A BufMut value is a cursor into the buffer. Writing to BufMut advances the cursor position.

The bytes crate was never designed with falible writes nor no_std targets in mind. This means that targets with little memory are forced to crash when memory is low, instead of gracefully handling errors.

Why the ByteEncoder trait instead of std::io::Write?

Because it's not available on no_std

Why the StrEncoder trait instead of std::fmt::Write?

Because std::fmt::Write is not implemented for a lot of types, most notably Vec<u8> or &mut [u8]. This means that you would have to use some sort of adapter in between to use these types as buffers. Furthermore, std::fmt::Write the error type is limited on what it can do:

The purpose of that error is to abort the formatting operation when the underlying destination encounters some error preventing it from accepting more text; in particular, it does not communicate any information about what error occurred. It should generally be propagated rather than handled, at least when implementing formatting traits.

Why did you build this?

  • Because there is no alternative, at least that i know of, that supports no_std properly
  • Because it easily lets you create TLV types
  • Because it's easier to work with than std::io::Write and std::fmt::Write
  • Because using format_args! with binary data often leads to a lot of boilerplate

BaseEncoder vs ByteEncoder vs StrEncoder

  • BaseEncoder: Provides abstraction and trait bounds. It is useful for building combinators that can work with any type of encoder.
  • StrEncoder: Provides a simple interface for encoding UTF-8 text. It is suitable for UTF-8 text output.
  • ByteEncoder: Provides full control over the encoding process. It is suitable for low-level encoding tasks, such as writing raw bytes to a buffer.

Dependencies

~74KB