Bitfield accessors

bearophile bearophileHUGS at lycos.com
Tue Nov 27 06:10:37 PST 2007


For reference, this is my last version so far, I think this solves both a bug, and makes it faster at runtime, but it needs much more testing:

// By Jarrett Billingsley <kb3ctd2 at yahoo.com>
// Improved by leonardo maffi, V1.1, Nov 26 2007
// This code needs much more testing

template Bitfield(alias data, Args...) {
    static assert(!(Args.length & 1), "Bitfield arguments must be an even number");
    static assert(data.sizeof*8 >= _SumLens!(Args),
                  "The sum of bit fields is bigger than data.sizeof*8");
    const char[] Bitfield = _BitfieldShim!((typeof(data)).stringof, data, Args).Ret;
}

private template _SumLens(Args...) {
    static if(Args.length == 0)
        const uint _SumLens = 0;
    else {
        static assert(Args[1] > 0, "Bitfield '" ~ Args[0] ~ "' is <= 0");
        const uint _SumLens = Args[1] + _SumLens!(Args[2 .. $]);
    }
}

// Odd bug in D templates -- putting "data.stringof" as a template argument
// gives it the string of the type, rather than the string of the symbol.
// This shim works around that.
private template _BitfieldShim(char[] typeStr, alias data, Args...) {
    const char[] Name = data.stringof;
    const char[] Ret = _BitfieldImpl!(data, typeStr, Name, 0, Args).Ret;
}

private template _BitfieldImpl(alias data, char[] typeStr, char[] nameStr, int offset, Args...) {
    static if(Args.length == 0)
        const char[] Ret = "";
    else {
        const char[] Name = Args[0];
        const int Size = Args[1];

        static if (data.sizeof < 8) {
            const int Mask = _bitmask32(Size);
            const char[] Shift1 = "0x" ~ _intToStr32(~(Mask << offset), 16);
        } else {
            const long Mask = _bitmask64(Size);
            const char[] Shift1 = "0x" ~ _intToStr64(~(Mask << offset), 16);
        }

        const char[] Getter = "public " ~ typeStr ~ " " ~ Name ~ "() { return (" ~
            nameStr ~ " >> " ~ _itoh(offset) ~ ") & " ~ _itoh(Mask) ~ "; }\n";

        const char[] Setter = "public void " ~ Name ~ "(" ~ typeStr ~ " val) { " ~
            nameStr ~ " = (" ~ nameStr ~ " & " ~ Shift1 ~
            ") | ((val & " ~ _itoh(Mask) ~ ") << " ~ _itoh(offset) ~ "); }\n\n";

        const char[] Ret = Getter ~ Setter ~
                           _BitfieldImpl!(data, typeStr, nameStr, offset + Size, Args[2 .. $]).Ret;
    }
}

private int _bitmask32(int size) {
    return (1 << size) - 1;
}

private long _bitmask64(long size) {
    return (1L << size) - 1;
}

private char[] _itoa(long i) {
    if (i < 0)
        return "-" ~ _intToStr64(-i, 10);
    else
        return _intToStr64(i, 10);
}

private char[] _itoh(long i) {
    return "0x" ~ _intToStr64(i, 16);
}

private char[] _intToStr32(uint i, int base) {
    const CONV_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz";
    if (i >= base)
        return _intToStr32(i / base, base) ~ CONV_CHARS[i % base];
    else
        return "" ~ CONV_CHARS[i % base];
}

private char[] _intToStr64(ulong i, int base) {
    const CONV_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz";
    if (i >= base)
        return _intToStr64(i / base, base) ~ CONV_CHARS[i % base];
    else
        return "" ~ CONV_CHARS[i % base];
}

//=======================================================================

struct SomeStruct {
    uint flags;
    mixin(Bitfield!(flags, "sign", 1, "biasedExponent", 8, "significand", 23));
}

import std.stdio;

void main() {
    uint flags;
    char[] r1a = Bitfield!(flags, "sign", 1, "biasedExponent", 8, "significand", 23);
    char[] r1b =
"public uint sign() { return (flags >> 0x0) & 0x1; }
public void sign(uint val) { flags = (flags & 0xfffffffe) | ((val & 0x1) << 0x0); }

public uint biasedExponent() { return (flags >> 0x1) & 0xff; }
public void biasedExponent(uint val) { flags = (flags & 0xfffffe01) | ((val & 0xff) << 0x1); }

public uint significand() { return (flags >> 0x9) & 0x7fffff; }
public void significand(uint val) { flags = (flags & 0x1ff) | ((val & 0x7fffff) << 0x9); }

";
    assert(r1a == r1b);

    //writefln( Bitfield!(flags, "sign", 1, "biasedExponent", 8, "significand", 24) ); // raises error

    ubyte otto;
    char[] r2a = Bitfield!(otto, "sign", 1, "seven", 7);
    char[] r2b =
"public ubyte sign() { return (otto >> 0x0) & 0x1; }
public void sign(ubyte val) { otto = (otto & 0xfffffffe) | ((val & 0x1) << 0x0); }

public ubyte seven() { return (otto >> 0x1) & 0x7f; }
public void seven(ubyte val) { otto = (otto & 0xffffff01) | ((val & 0x7f) << 0x1); }

";
    assert(r2a == r2b);

    ubyte d1;
    writefln(Bitfield!(d1, "f1", 3, "s1", 5));

    ushort d2;
    writefln(Bitfield!(d2, "f2", 9, "s2", 7));

    uint d3;
    writefln(Bitfield!(d3, "f3", 17, "s3", 15));

    ulong d4;
    writefln(Bitfield!(d4, "f4", 31, "s4", 33));
}

Bye,
bearophile


More information about the Digitalmars-d-learn mailing list