pub trait NopFlip: Flip { }
Expand description

Defines types whose values may not be endian-flipped.

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).

Description

A struct type may contain a union type as its member. A union type can be encasted and decasted but cannot be automatically endian-flipped. In order to automatically flip the endianness of such container struct type without flipping the endianness of its internal union type, #[derive(NopFlip)] is provided.

#[derive(NopFlip)] can be declared for a struct type or a union type whose all members implement Flip. It nominally implements trait Flip whose methods do nothing (Nop = No operation). It also implements trait NopFlip.

Trait NopFlip has no method. It is purely defined as a trait bound.

Example 1

In the example below, #[derive(NopFlip)] nominally implements trait Flip whose methods do nothing (Nop = No operation) for UnionB so that method encastf and method decastf can flip the endianness of the container struct StructC except its internal union UnionB.

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_u8, 0x11]);
assert_eq!(var2_c.a.y, 0x1312);
assert_eq!(var2_c.a.z, 0x17161514);

// Check the results (UnionB in StructC)
unsafe {
    assert_eq!(var2_c.b.u, [0x18_u8, 0x19, 0x1A, 0x1B]);
    if cfg!(target_endian = "little") {
        assert_eq!(var2_c.b.v, [0x1918_u16, 0x1B1A]);
        assert_eq!(var2_c.b.w, 0x1B1A1918);
    } else if cfg!(target_endian = "big") {
        assert_eq!(var2_c.b.v, [0x1819_u16, 0x1A1B]);
        assert_eq!(var2_c.b.w, 0x18191A1B);
    }
}

// Check the result (f32 in StructC)
assert_eq!(var2_c.f, 12.5_f32);

// Check the result (output3)
assert_eq!(size3, 16);
assert_eq!(&output3.into_inner(), &bytes1[..]);

In the example above, method encastf decodes bytes in bytes1 in little-endian (LE) to variable var2_c of type StructC. Then, method decastf encodes the resulting value in var2_c to bytes in little-endian (LE) and stores them in bytes3.

As the results show, field a and f of StructC are endian-flipped, but field b is not endian-flipped. The endianness of the value(s) in field b needs to be flipped manually, e.g. by using the methods provided by trait Flip.

Example 2

#[derive(NopFlip)] can be declared for a struct type in order to keep the original value. In the example below, FatMagic is a struct type. It contains a magic number which indicates whether native-endian or swapped-endian. Keeping the orignal value may be a help in certain situations.

use castflip::{Cast, Flip, NopFlip, Endian, EncastMem, DecastMem};

// struct fat_header and magic numbers are defined in <mach-o/fat.h> at
// /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/

#[repr(C)]
#[derive(Cast, NopFlip)]
struct FatMagic {
    number: u32,
}

const FAT_MAGIC32: u32 = 0xCAFEBABE;  // Native Endian, 32-bit
const FAT_MAGIC64: u32 = 0xCAFEBABF;  // Native Endian, 64-bit
const FAT_CIGAM32: u32 = 0xBEBAFECA;  // Swapped Endian, 32-bit
const FAT_CIGAM64: u32 = 0xBFBAFECA;  // Swapped Endian, 64-bit

#[repr(C)]
#[derive(Cast, Flip)]
struct FatHeader {
    magic:     FatMagic,  // Magic number (See above)
    nfat_arch: u32,       // Number of architectures in this file.
}

// Input data (8 bytes)
let bytes1: [u8; 8] = [0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x02];

// Decode the first 4 bytes in `bytes1` without endian-flipping
// to determine the endianness of `FatHeader`.
// (The generic type parameter `u32` is omitted because it can be inferred
//  from the fact that the return value is compared with u32 constants)
let endian = match bytes1.encast()? {
    FAT_MAGIC32 | FAT_MAGIC64 => Endian::Native,
    FAT_CIGAM32 | FAT_CIGAM64 => Endian::Swapped,
    _ => panic!(),
};

// Decode bytes `bytes1` to variable `fat_hdr2` of type `FatHeader`.
let fat_hdr2: FatHeader = bytes1.encastf(endian)?;

// Check the result (endian); The magic number says it is in big-endian.
assert_eq!(endian.absolute(), Endian::Big);

// Check the results (fat_hdr2); `FatMagic` is not endian-flipped.
if cfg!(target_endian = "little") {
    assert_eq!(fat_hdr2.magic.number, FAT_CIGAM32);
} else if cfg!(target_endian = "big") {
    assert_eq!(fat_hdr2.magic.number, FAT_MAGIC32);
}
assert_eq!(fat_hdr2.nfat_arch, 2);  // This number is endian-flipped.

The example above decodes the first 8 bytes of the fat header located at the head of the Mach-o file format. It has two fields: magic number (4 bytes) and the number of architectures in the file (4 bytes).

At first, method encast decodes the first 4 bytes in bytes1 without endian-flipping to determine the endianness of FatHeader. Then, method encastf decodes the first 8 bytes in bytes1 to variable fat_hdr2 of FatHeader by using the determined endianness. Note that the first 4 bytes (i.e., magic number) are read twice in this example for simplicity.

Note: Mach-o is a file format for executable files. It is used by most systems based on the Mach kernel.

Implementors