JeffreySarnoff
8/17/2017 - 3:57 AM

BitOps for Julia

BitOps for Julia

"""
BitOps exports functions using low level bit manipulation to modify standard Int and UInt values.
Note:  Functions that check their arguments are named `safe_..`, others do no checking.

Copyright 2017 by Jeffrey Sarnoff.  Released under the MIT License.
"""
module BitOps

using Compat

export bitsizeof, clr_bit, set_bit, flp_bit, 
       iso_bit, get_bit, put_bit,
       clr_lsbits, set_lsbits, clr_msbits, set_msbits,
       mask_bits, filt_bits

#if VERSION < v"0.6.0-alpha"
#  xor(a,b) = a$b
#end
       
"bit size of type or value"
bitsizeof{T}(::Type{T}) = sizeof(T) << 3
bitsizeof{T}(x::T) = bitsof(T)

const SUN = Union{Signed, Unsigned}
const INT = Union{Int32, Int64}        # bit manipulations with Int32 shifts tend to be faster

set_bit{T<:SUN, I<:INT}(::Type{T}, bitidx::I) =   one(T) << bitidx
clr_bit{T<:SUN, I<:INT}(::Type{T}, bitidx::I) = ~(one(T) << bitidx)

@compat flp_bit{T<:SUN, I<:INT}(x::T, bitidx::I) = xor(x, set_bit(T, bitidx))

iso_bit{T<:SUN, I<:INT}(x::T, bitidx::I) = x & set_bit(T, bitidx)
get_bit{T<:SUN, I<:INT}(x::T, bitidx::I) = (iso_bit(x, bitidx) >> bitidx) == one(T)
put_bit{T<:SUN, I<:INT}(x::T, bitidx::I, bitvalue::Bool) = (x & clr_bit(T, bitidx)) | (bitvalue << bitidx)


set_lsbits{T<:SUN, I<:INT}(::Type{T}, bitidx::I) =   (one(T) << bitidx) - one(T)
clr_lsbits{T<:SUN, I<:INT}(::Type{T}, bitidx::I) = ~((one(T) << bitidx) - one(T))
clr_msbits{T<:SUN, I<:INT}(::Type{T}, bitidx::I) =   ~zero(T) >>> bitidx
set_msbits{T<:SUN, I<:INT}(::Type{T}, bitidx::I) = ~(~zero(T) >>> bitidx)

# inclusive of lobitidx and hibitidx
mask_bits{T<:SUN, I<:INT}(::Type{T}, lobitidx::I, hibitidx::I) =   set_lsbits(T, hibitidx-lobitidx+1) << lobitidx
filt_bits{T<:SUN, I<:INT}(::Type{T}, lobitidx::I, hibitidx::I) = ~(set_lsbits(T, hibitidx-lobitidx+1) << lobitidx)

set_lsbits{T<:SUN, I<:INT}(x::T, bitidx::I) = x | set_lsbits(T, bitidx)
clr_lsbits{T<:SUN, I<:INT}(x::T, bitidx::I) = x & clr_lsbits(T, bitidx)
set_msbits{T<:SUN, I<:INT}(x::T, bitidx::I) = x | set_msbits(T, bitidx)
clr_msbits{T<:SUN, I<:INT}(x::T, bitidx::I) = x & clr_msbits(T, bitidx)

mask_bits{T<:SUN, I<:INT}(x::T, lobitidx::I, hibitidx::I) = x & mask_bits(T, lobitidx, hibitidx)
filt_bits{T<:SUN, I<:INT}(x::T, lobitidx::I, hibitidx::I) = x & filt_bits(T, lobitidx, hibitidx)


import Base: sign_mask, exponent_mask, significand_mask,
             exponent_bits, significand_bits,
             (~), (&), (|)


for (U,S) in [(:UInt8, :Int8), (:UInt16, :Int16), (:UInt32, :Int32), (:UInt64, :Int64), (:UInt128, :Int128)]
    @eval begin
        Base.reinterpret(::Type{Signed}, x::$U) = reinterpret($S, x)
        Base.reinterpret(::Type{Signed}, x::$S) = x
        Base.reinterpret(::Type{Unsigned}, x::$S) = reinterpret($U, x)
        Base.reinterpret(::Type{Unsigned}, x::$U) = x
    end
end

end # module BitOps