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 encastable / decastable, and #[derive(Flip)]
makes the value of UdpHdr endian-flippable.
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
encastable / decastable, #[derive(Flip)] makes StructA
and StructC endian-flippable, and #[derive(NopFlip)] marks
UnionB as endian-flippable 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 encasted / decasted 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 encastable / decastable, and #[derive(Flip)] makes
the values of Pair endian-flippable.
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
Castmarks types asencastable /decastableFlipmarks types asendian-flippableNopFlipmarks types asendian-flippable 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
encastdecodes a required number of bytes to a value.encastfdecodes a required number of bytes to a value withendian-flipping.encastsdecodes a required number of bytes to a slice of value(s).encastsfdecodes a required number of bytes to a slice of value(s) withendian-flipping.encastvdecodes a required number of bytes to a vector of value(s).encastvfdecodes a required number of bytes to a vector of value(s) withendian-flipping.
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
decastencodes a variable to a number of bytes.decastfencodes a variable to a number of bytes withendian-flipping.decastsencodes a slice of variable(s) to a number of bytes.decastsfencodes a slice of variable(s) to a number of bytes withendian-flipping.
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§
- experimental
- Defines experimental traits and historical traits.
Enums§
- Endian
- Defines two types of endianness: relative endian (Native or Swapped) and absolute endian (Little or Big).
Constants§
- BE
- An alias of
Endian::Big, which means Big-Endian. - LE
- An alias of
Endian::Little, which means Little-Endian. - NE
- An alias of
Endian::Native, which means Native-Endian. - SE
- An alias of
Endian::Swapped, which means Swapped-Endian.
Traits§
- Cast
- Defines types whose values can be
encasted anddecasted. - DecastIO
- Defines methods to
decastandendian-flipthroughio::Write. - Decast
Mem - Defines methods to
decastandendian-flipon memory. - EncastIO
- Defines methods to
encastandendian-flipthroughio::Read. - Encast
Mem - Defines methods to
encastandendian-flipon memory. - Flip
- Defines types whose values can be
endian-flipped. - NopFlip
- Defines types whose values may not be
endian-flipped.