I am happy to announce that we open-sourced PackedStructs.jl.
Shamelessly copying from its README:
PackedStructs.jl provides the @packed macro to annotate structs. These structs will pack together types which do not have a native size, i.e. a power-of-two byte size. This is especially useful when using BitIntegers.jl or EmulatedBitIntegers.jl. Accesses to these packed types take some additional CPU cycles in general. Therefore, speed is effectively traded for space. However, the space savings can lead to a better caching behavior. In some situations, packed structs can be both smaller and faster than regular structs.
Usage
To create a packed struct, do, e.g.:
julia> using EmulatedBitIntegers, PackedStructs
julia> @emulate Int4
julia> @packed struct Foo
first::Int4
second::Int4
end
This struct is smaller than it would be without being @packed.
julia> struct RegularFoo
first::Int4
second::Int4
end
julia> Foo |> sizeof
1
julia> RegularFoo |> sizeof
2
This type can be used like a regular struct, so you can create values by
julia> foo = Foo(2, -1)
Foo(2, -1)
and you can access values by
julia> foo.first
2
julia> foo.second
-1
They have the correct type
julia> foo.first |> typeof
Int4
Internally, the bits of the different fields immediately follow each other
julia> (reinterpret(UInt8, foo), foo.first, foo.second) .|> bitstring
("00101111", "0010", "1111")
Overall, I think the package gets as close to a native implementation as you can get in a package. If you use the internal low-level functions (like getfield instead of the (implicit) getproperty), you can see the wrappers leaking, but otherwise usage should feel totally native (as seen above).
Thanks to the nice people in JuliaData, who kindly allowed me to move and maintain the code there.
This package has some similarities to FieldFlags.jl. However, FieldFlags.jl focuses more on logical bits (fields), whereas PackedStructs.jl with EmulatedBitIntegers.jl focuses more on integers.