1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use crate::fmt::{FmtArg, FmtKind, NumberFmt};

/// A version of FmtArg which occupies less space, but needs to be unpacked to be used.
#[derive(Copy, Clone)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
pub struct PackedFmtArg {
    indentation: u8,
    bitfields: u8,
}

const FMT_KIND_OFFSET: u8 = 1;
const NUMBER_FMT_OFFSET: u8 = FMT_KIND_OFFSET + FmtKind::BITS;

impl FmtArg {
    /// Converts this `FmtArg` into a `PackedFmtArg`,
    /// which is smaller but can only be converted back into a `FmtArg`.
    #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
    pub const fn pack(self) -> PackedFmtArg {
        let Self {
            indentation,
            is_alternate,
            fmt_kind,
            number_fmt,
        } = self;

        PackedFmtArg {
            indentation,
            bitfields: is_alternate as u8
                | ((fmt_kind as u8) << FMT_KIND_OFFSET)
                | ((number_fmt as u8) << NUMBER_FMT_OFFSET),
        }
    }
}

impl PackedFmtArg {
    /// Converts this `PackedFmtArg` back into a `FmtArg`.
    pub const fn unpack(self) -> FmtArg {
        let indentation = self.indentation;
        let is_alternate = (self.bitfields & 1) != 0;
        let fmt_kind = FmtKind::from_prim(self.bitfields >> FMT_KIND_OFFSET);
        let number_fmt = NumberFmt::from_prim(self.bitfields >> NUMBER_FMT_OFFSET);

        FmtArg {
            indentation,
            is_alternate,
            fmt_kind,
            number_fmt,
        }
    }
}

macro_rules! enum_prim {
    (
        $type:ident, $bits:expr;
        default $default:ident,
        $($variant:ident),*
        $(,)?
    ) => (
        #[allow(non_upper_case_globals)]
        const _: () = {
            $(const $variant: u8 = $type::$variant as u8;)*
            const __MASK: u8 = 2u8.pow($bits) - 1;

            match $type::$default {
                $($type::$variant => (),)*
                $type::$default => (),
            }

            impl $type {
                const BITS: u8 = $bits;

                const fn from_prim(n: u8) -> Self {
                    match n & __MASK {
                        $($variant => Self::$variant,)*
                        _ => Self::$default,
                    }
                }
            }
        };
    )
}
use enum_prim;

enum_prim! {
    FmtKind, 2;
    default Debug,
    Display,
}

enum_prim! {
    NumberFmt, 2;
    default Decimal,
    Binary,
    Hexadecimal,
}