This is quite hacky, but it definitely unblocked me while working on FIT.jl. In Garmin FIT files, each record has a byte header used to identify the message type. At position 6 in the byte header, we find the bit used to identify whether a message is a data message or a definition.
I know there are ways to isolate this bit using bitwise operators, but I settled on the below approach (knowing that this software at the moment is purely for myself).
parse(UInt8, bitstring(byte)[idx])
Why is this horribly inefficent?
Well, we are converting the byte into a string, indexing into the string grabbing a specific char, then converting back to our UInt8 type using `parse`. Wow, not great.
But it solves the problem!
Julia is an interactive language, you develop mainly within the REPL. So, having my single `bit_at` function be powered by this hacky method, I can easily swap out the impelementation later when needed.
One more cool feature, ranges.
In Julia, you can use a bit of syntactic sugar to define a range.
range = 1:1:8
This range means start at 1, end at 8, and have a step size of 1. So, I was able to extend this concept of ranges to implement the concept of little vs. big-endian in my bit-at code. Observe:
function bit_vector(byte::UInt8; little_endian=false)::Vector{UInt8}
range = if little_endian
8:-1:1
else
1:1:8
end
s = bitstring(byte)
[parse(UInt8, s[b]) for b in range]
end
The vector of “bits” is generated using the list comprehension at the bottom of the function. `for b in range` is of special importance. I use `b` as the index of the bitstring `s`, and run that bit through my parse call described above. This gives me my desired effect of being a vector of “bits”, represented as `UInt8` bytes.
In action:
julia> bit_vector(0x40)
8-element Vector{UInt8}:
0x00
0x01
0x00
0x00
0x00
0x00
0x00
0x00