Expand description
This crate provides methods for encoding and decoding numeric variables, arrays and structures in little-endian and big-endian. They can be nested and arranged in a series.
Note: In this crate, the term encast
means decoding a number of
bytes to one or more values, the term decast
means encoding one or
more variables to a number of bytes, and the term endian-flip
means
flipping the endianness of value(s).
Example 1
In the example below, method encastf
decodes bytes in bytes1
in
big-endian (BE
) to variable udp_hdr2
of type UdpHdr
. Then,
method decastf
encodes the resulting value in udp_hdr2
to bytes in
big-endian (BE
) and stores them in bytes3
.
use castflip::{Cast, Flip, EncastMem, DecastMem, BE};
#[repr(C)]
#[derive(Cast, Flip)]
struct UdpHdr { // UDP: https://www.rfc-editor.org/rfc/rfc768.txt
sport: u16, // UDP Source Port
dport: u16, // UDP Destination Port
len: u16, // UDP Length
sum: u16, // UDP Checksum
}
// Input data: UDP header (8 bytes)
let bytes1: [u8; 8] = [0xC3, 0xC9, 0x00, 0x35, 0x00, 0x32, 0x82, 0x3F];
// Decode bytes `bytes1` to variable `udp_hdr2`.
let udp_hdr2 = bytes1.encastf::<UdpHdr>(BE)?; // BE = Big-Endian
// Encode the resulting UDP header `udp_hdr2` to bytes `bytes3`.
let mut bytes3 = [0_u8; 8];
let size3 = bytes3.decastf::<UdpHdr>(&udp_hdr2, BE)?;
// Check the results (udp_hdr2)
assert_eq!(udp_hdr2.sport, 0xC3C9); // = 50121
assert_eq!(udp_hdr2.dport, 0x0035); // = 53 (DNS)
assert_eq!(udp_hdr2.len, 0x0032); // = 50
assert_eq!(udp_hdr2.sum, 0x823F);
// Check the results (bytes3)
assert_eq!(size3, 8);
assert_eq!(bytes3, bytes1);
In the example above, #[derive(
Cast
)]
makes the value of
UdpHdr
encast
able / decast
able, and #[derive(
Flip
)]
makes the value of UdpHdr
endian-flip
pable.
Trait EncastMem
provides methods to encast
from memory, and
trait DecastMem
provides methods to decast
to memory. The
generic type parameters of their methods can be omitted where the Rust
compiler can infer them. Their methods whose names end with ‘f’ flip
the endianness of the results. The endianness of bytes is specified
in their argument. BE
is an alias of Endian
::Big
, which
means Big-Endian.
Note: UDP is one of the fundamental protocols in the internet protocol suite.
Example 2
Arrays, structures and unions can be nested as in the example below.
use std::io::Cursor;
use castflip::{Cast, Flip, NopFlip, EncastIO, DecastIO, LE};
#[repr(C)]
#[derive(Cast, Flip)]
struct StructA { // 8 bytes (total)
x: [u8; 2], // 2 bytes
y: u16, // 2 bytes
z: u32, // 4 bytes
}
#[repr(C)]
#[derive(Cast, NopFlip)]
union UnionB { // 4 bytes (largest)
u: [u8; 4], // 4 bytes
v: [u16; 2], // 4 bytes
w: u32, // 4 bytes
}
#[repr(C)]
#[derive(Cast, Flip)]
struct StructC { // 16 bytes (total)
a: StructA, // 8 bytes
b: UnionB, // 4 bytes
f: f32, // 4 bytes
}
// Input data (16 bytes)
let bytes1: [u8; 16] = [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x00, 0x00, 0x48, 0x41];
let mut input1 = Cursor::new(bytes1);
// Decode input `input1` to variable `var2_c` of type `StructC`.
let var2_c: StructC = input1.encastf(LE)?; // LE = Little-Endian
// Encode variable `var2_c` to bytes and write them to `output3`.
let mut output3 = Cursor::new(vec![0_u8; 16]);
let size3 = output3.decastf(&var2_c, LE)?;
// Check the results (StructA in StructC)
assert_eq!(var2_c.a.x, [0x10, 0x11]); // x: [u8; 2],
assert_eq!(var2_c.a.y, 0x1312); // y: u16,
assert_eq!(var2_c.a.z, 0x17161514); // z: u32,
// Check the results (UnionB in StructC)
unsafe {
assert_eq!(var2_c.b.u, [0x18, 0x19, 0x1A, 0x1B]); // u: [u8; 4],
if cfg!(target_endian = "little") {
assert_eq!(var2_c.b.v, [0x1918, 0x1B1A]); // v: [u16; 2],
assert_eq!(var2_c.b.w, 0x1B1A1918); // w: u32,
} else if cfg!(target_endian = "big") {
assert_eq!(var2_c.b.v, [0x1819, 0x1A1B]); // v: [16; 2],
assert_eq!(var2_c.b.w, 0x18191A1B); // w: u32,
}
}
// Check the result (f32 in StructC)
assert_eq!(var2_c.f, 12.5_f32); // f: f32,
// Check the results (output3)
assert_eq!(size3, 16);
assert_eq!(&output3.into_inner(), &bytes1[..]);
In the example above, #[derive(
Cast
)]
makes all types
encast
able / decast
able, #[derive(
Flip
)]
makes StructA
and StructC
endian-flip
pable, and #[derive(
NopFlip
)]
marks
UnionB
as endian-flip
pable but the implemented operation is Nop
(No operation).
Note that io::Cursor
wraps an in-memory buffer and provides it
through io::Read
and io::Write
.
Trait EncastIO
provides methods to encast
from io::Read
, and
trait DecastIO
provides methods to decast
to io::Write
. The
type of the value(s) can be explicitly specified as the generic type
parameter of their methods or implicitly specified so that the Rust
compiler can infer. Their methods whose names end with ‘f’ flip the
endianness of the results. The endianness of bytes is specified in
their argument. LE
is an alias of Endian
::Little
, which
means Little-Endian.
Example 3
A series of structured binary data can be encast
ed / decast
ed as
in the example below.
use castflip::{Cast, Flip, EncastMem, DecastMem, BE};
#[repr(C)]
#[derive(Cast, Flip, PartialEq, Debug)]
struct Pair (u16, u16); // 4 bytes
// Input data (12 bytes)
let bytes1: [u8; 12] = [0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B];
// Decode bytes `bytes1` to variable `vec2`.
let vec2: Vec<Pair> = bytes1.encastvf(3, BE)?; // 3 pairs, BE = Big-Endian
// Encode variable `vec2` to bytes `bytes3`.
let mut bytes3 = [0_u8; 12];
let size3 = bytes3.decastsf(&vec2, BE)?;
// Check the result (vec2)
assert_eq!(vec2, vec![Pair(0x2021, 0x2223), Pair(0x2425, 0x2627),
Pair(0x2829, 0x2A2B)]);
// Check the results (bytes3)
assert_eq!(size3, 12);
assert_eq!(bytes3, bytes1);
In the example above, #[derive(
Cast
)]
makes the values of
Pair
encast
able / decast
able, and #[derive(
Flip
)]
makes
the values of Pair
endian-flip
pable.
Trait EncastMem
provides methods to encast
from memory, and
trait DecastMem
provides methods to decast
to memory. The type
of the value(s) can be explicitly specified as the generic type
parameter of their methods or implicitly specified so that the Rust
compiler can infer. The methods whose name contain ‘s’ (= slice) or
‘v’ (= vector) encast
/ decast
a series of structured binary data,
and the methods whose names end with ‘f’ flip the endianness of the
results. The number of elements is specified in the argument of
method encastvf
. The endianness of bytes is specified in their
argument. BE
is an alias of Endian
::Big
, which means
Big-Endian.
Note: The reason why #[derive(PartialEq, Debug)]
is declared is that
assert_eq! requires them. This crate works without them.
Summary
This crate provides methods for encoding and decoding numeric variables, arrays and structures in little-endian and big-endian. They can be nested and arranged in a series.
Two types of endianness is defined in enum Endian
: relative endian
(Native or Swapped) and absolute endian (Little or Big).
List of traits to mark characteristics of types
Cast
marks types asencast
able /decast
ableFlip
marks types asendian-flip
pableNopFlip
marks types asendian-flip
pable but the implemented operation is Nop (No operation)
They can be implemented by declaring #[derive(
Cast
)]
,
#[derive(
Flip
)]
and #[derive(
NopFlip
)]
respectively if
required conditions are met.
List of methods defined in trait EncastMem
and trait EncastIO
encast
decodes a required number of bytes to a value.encastf
decodes a required number of bytes to a value withendian-flip
ping.encasts
decodes a required number of bytes to a slice of value(s).encastsf
decodes a required number of bytes to a slice of value(s) withendian-flip
ping.encastv
decodes a required number of bytes to a vector of value(s).encastvf
decodes a required number of bytes to a vector of value(s) withendian-flip
ping.
The methods defined in trait EncastMem
returns Option
and the
methods defined in trait EncastIO
returns io::Result
. If
successful, they return resulting value(s) for a value or a vector, or
they return the number of bytes for a slice. The endianness of the
bytes in self
is specified in the arguments of encastf
, encastsf
and encastvf
. The number of elements in the resulting vector is
specified in the arguments of encastv
and encastvf
.
List of methods defined in trait DecastMem
and trait DecastIO
decast
encodes a variable to a number of bytes.decastf
encodes a variable to a number of bytes withendian-flip
ping.decasts
encodes a slice of variable(s) to a number of bytes.decastsf
encodes a slice of variable(s) to a number of bytes withendian-flip
ping.
The methods defined in trait DecastMem
returns Option
and the
methods defined in trait DecastIO
returns io::Result
. If
successful, they return the number of resulting bytes. The endianness
of resulting bytes is specified in the arguments of decastf
and
decastsf
. (FYI: decastv
and decastvf
is renamed to decasts
and decastsf
, respectively)
Notes on trait EncastIO
and trait DecastIO
EncastIO
provides methods to encast
from io::Read
, and
DecastIO
provides methods decast
to io::Write
. Remember
that io::Read
and io::Write
are implemented also for &[u8]
.
Additionally, io::Cursor
wraps an in-memory buffer and provides it
through io::Read
and io::Write
. Therefore, EncastIO
and
DecastIO
would enable to write a library that can encast
from
and decast
to file, network and memory.
Notes on #![no_std]
This crate has features std
and alloc
that are enabled by default.
To build this crate for a no_std
environment without a global
allocator, add the following dependencies in Cargo.toml
. With the
current version of this crate, EncastMem
and DecastMem
except
methods encastv
and encastvf
are available.
[dependencies]
castflip = { version = "0.1", default-features = false }
To build this crate for a no_std
environment with a global allocator,
add the following dependencies in Cargo.toml
. With the current
version of this crate, EncastMem
and DecastMem
including
methods encastv
and encastvf
are available. (allocator_api
is
not supported because it cannot be used on the stable release channel.)
[dependencies]
castflip = { version = "0.1", default-features = false, features = ["alloc"] }
In both cases above, EncastIO
and DecastIO
cannot be used
since they require std::io
.
Because we do not have good no_std
environments nor good experiences,
we are not sure that the current version of this crate provides
sufficient features for no_std
environments. Feedbacks are welcome!
For more information, please see the description of each trait and enum listed below.
Modules
Defines experimental traits and historical traits.
Enums
Defines two types of endianness: relative endian (Native or Swapped) and absolute endian (Little or Big).
Constants
An alias of Endian::Big
, which means Big-Endian.
An alias of Endian::Little
, which means Little-Endian.
An alias of Endian::Native
, which means Native-Endian.
An alias of Endian::Swapped
, which means Swapped-Endian.
Traits
Defines types whose values can be encast
ed and decast
ed.
Defines methods to decast
and endian-flip
on memory.
Defines methods to encast
and endian-flip
on memory.
Defines types whose values can be endian-flip
ped.
Defines types whose values may not be endian-flip
ped.