ivory-bitdata-0.2.0.0: src/Ivory/BitData/BitData.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE UndecidableInstances #-}
--
-- BitData.hs --- Typed bit data types.
--
-- Copyright (C) 2013, Galois, Inc.
-- All Rights Reserved.
--
module Ivory.BitData.BitData where
import GHC.TypeLits
import Ivory.Language
import Ivory.BitData.Bits
-- | Class of bit data types defined by the "bitdata" quasiquoter.
class (SingI (BitSize (BitType a)),
IvoryRep (BitDataRep a),
BitType a ~ Bits (BitSize (BitType a))) => BitData a where
-- | Return the base "(Bits n)" type as defined in the "bitdata"
-- quasiquoter.
type BitType a :: *
-- | Convert a bit data type to its raw bit value. This is always
-- well defined and should be exported.
toBits :: a -> BitType a
-- | Convert a raw bit value to a bit data type. All values may not
-- be well defined with respect to the original set of bit data
-- constructors. For now, we allow these "junk" values to be
-- created, but that may change in the future (perhaps by
-- implementing a checked, Ivory run-time conversion).
fromBits :: BitType a -> a
-- | The Ivory type that stores the actual value for a bit data type.
--
-- This is a shorthand to simplify the constraints on functions that
-- take arguments of the "BitData" class.
type BitDataRep a = BitRep (BitSize (BitType a))
-- | Convert a raw Ivory type to a bit data type. If the input value
-- is too large, the out of range upper bits will be masked off.
fromRep :: BitData a => BitDataRep a -> a
fromRep = fromBits . repToBits
-- XXX do not export---used when unwrapping/rewrapping values when
-- setting fields and the size has obviously not changed.
unsafeFromRep :: BitData a => BitDataRep a -> a
unsafeFromRep = fromBits . unsafeRepToBits
-- | Convert a bit data value to its Ivory representation.
toRep :: BitData a => a -> BitDataRep a
toRep = unBits . toBits
-- | Identity instance of "BitData" for the base "Bits n" type.
instance (SingI n, IvoryRep (BitRep n)) => BitData (Bits n) where
type BitType (Bits n) = Bits n
toBits = id
fromBits = id
-- | Description of a bit field defined by the "bitdata" quasiquoter.
-- Each field defined in the record syntax will generate a top-level
-- definition of "BitDataField".
--
-- This constructor must remain unexported so that only fields checked
-- by the quasiquoter are created.
data BitDataField a b = BitDataField
{ bitDataFieldPos :: Int
, bitDataFieldLen :: Int
} deriving Show
-- | Bit data field composition. (like Control.Category.>>>)
(#>) :: BitDataField a b -> BitDataField b c -> BitDataField a c
(BitDataField p1 _) #> (BitDataField p2 l2) = BitDataField pos len
where
pos = p1 + p2
len = l2
-- | Extract a field from a bit data definition. Returns the value as
-- the type defined on the right hand side of the field definition in
-- the "bitdata" quasiquoter.
getBitDataField :: (BitData a, BitData b,
BitCast (BitDataRep a) (BitDataRep b))
=> BitDataField a b -> a -> b
getBitDataField f x = unsafeFromRep (bitCast ((toRep x `iShiftR` pos) .& mask))
where pos = fromIntegral (bitDataFieldPos f)
mask = fromIntegral ((2 ^ bitDataFieldLen f) - 1 :: Integer)
-- | Infix operator to read a bit data field. (like Data.Lens.^.)
(#.) :: (BitData a, BitData b,
BitCast (BitDataRep a) (BitDataRep b))
=> a -> BitDataField a b -> b
(#.) = flip getBitDataField
-- | Set a field from a bit data definition.
setBitDataField :: (BitData a, BitData b,
SafeCast (BitDataRep b) (BitDataRep a))
=> BitDataField a b -> a -> b -> a
setBitDataField f x y = unsafeFromRep ((toRep x .& mask) .| val)
where val = safeCast (toRep y) `iShiftL` pos
pos = fromIntegral (bitDataFieldPos f)
fmax = fromIntegral ((2 ^ bitDataFieldLen f) - 1 :: Integer)
mask = iComplement (fmax `iShiftL` pos)
-- | Set a single-bit field in a bit data value.
setBitDataBit :: BitData a => BitDataField a Bit -> a -> a
setBitDataBit f x = unsafeFromRep (toRep x .| (1 `iShiftL` pos))
where pos = fromIntegral (bitDataFieldPos f)
-- | Clear a single-bit field in a bit data value.
clearBitDataBit :: BitData a => BitDataField a Bit -> a -> a
clearBitDataBit f x = unsafeFromRep (toRep x .& (iComplement (1 `iShiftL` pos)))
where pos = fromIntegral (bitDataFieldPos f)