Converting BitVector arbitrary length components into UInt64, Float64, etc

Hey all! New to Julia and was looking apply a use case I have to better understand the language. I have binary packet data similar to a network packet ethernet/ip/tcp where I need to be able to convert packets and their respective layers. The complication of this problem is I need to be able to account for endian-ness as well as I’ll want to convert a 3 bit unisigned int into preferably an UInt64. I’m interested in julia on how I could use macros to generate the struct as well as the logic to parse. e.g.

@parse (:EthernetFrame, :bigedian, [
     Field(:destination, UInt16, offset=0, length=48),
     Field(:ethtype, UInt8, offset=96, length=16),
     ...
])

Notice how some bits are not used. Ideally this would generate

struct EthernetFrame
    destination::UInt16
    ethtype::UInt8
end

# function to parse bitvector to create struct from description.
EthernetPacket(data::BitVector) = ... 

Any ideas on how to convert say a BitVector of length 3 bits to a UInt64, Float64, etc.?

Would this require masking and is there a performant way in Julia?

Recently used simple for loop to write the bits, setting thing with inplace-bitwiseor-updates |= and bitshifts (<< , >>>). Something like

function packbits(::Type{T<:Int}, stream) where T
dest = zero(T)
for (b, bool) in enumerate(stream)
if bool
    dest |= one(T) << (b - 1)
end
end
return dest
end

“T” is your Int type. endian information could also be passed as type parameters in a similar way so the branch is compiled away. Could probably yet faster if you are more careful about your iteration.

I think the type system has enough power without macros to cover all your bases in a performant way but I am not a macro-user by the rule. Tricky to cover signed and unsigned stuff at the same time. Looks like a weekend CompSci homework problem :stuck_out_tongue: