{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Haskus.Binary.BitField
( BitFields (..)
, bitFieldsBits
, BitField (..)
, extractField
, extractField'
, updateField
, updateField'
, withField
, withField'
, matchFields
, matchNamedFields
, Field
)
where
import Haskus.Binary.BitSet as BitSet
import Haskus.Binary.Enum
import Haskus.Number.Word
import Haskus.Number.Int
import Haskus.Binary.Bits
import Haskus.Binary.Storable
import Haskus.Utils.HList
import Haskus.Utils.Types
import Haskus.Utils.Tuple
newtype BitFields b (f :: [*]) = BitFields b deriving (Storable)
bitFieldsBits :: BitFields b f -> b
{-# INLINABLE bitFieldsBits #-}
bitFieldsBits (BitFields b) = b
newtype BitField (n :: Nat) (name :: Symbol) s = BitField s deriving (Storable)
type family Offset (name :: Symbol) fs :: Nat where
Offset name (BitField n name s ': xs) = AddOffset xs
Offset name (BitField n name2 s ': xs) = Offset name xs
type family AddOffset fs :: Nat where
AddOffset '[] = 0
AddOffset (BitField n name s ': xs) = n + AddOffset xs
type family Output (name :: Symbol) fs :: * where
Output name (BitField n name s ': xs) = s
Output name (BitField n name2 s ': xs) = Output name xs
type family Size (name :: Symbol) fs :: Nat where
Size name (BitField n name s ': xs) = n
Size name (BitField n name2 s ': xs) = Size name xs
type family WholeSize fs :: Nat where
WholeSize '[] = 0
WholeSize (BitField n name s ': xs) = n + WholeSize xs
type family BitFieldTypes xs where
BitFieldTypes '[] = '[]
BitFieldTypes (BitField n name s ': xs) = s ': BitFieldTypes xs
class Field f where
fromField :: Integral b => f -> b
toField :: Integral b => b -> f
instance Field Bool where
fromField True = 1
fromField False = 0
toField 0 = False
toField _ = True
instance Field Word where
fromField = fromIntegral
toField = fromIntegral
instance Field Word8 where
fromField = fromIntegral
toField = fromIntegral
instance Field Word16 where
fromField = fromIntegral
toField = fromIntegral
instance Field Word32 where
fromField = fromIntegral
toField = fromIntegral
instance Field Word64 where
fromField = fromIntegral
toField = fromIntegral
instance Field Int where
fromField = fromIntegral
toField = fromIntegral
instance Field Int8 where
fromField = fromIntegral
toField = fromIntegral
instance Field Int16 where
fromField = fromIntegral
toField = fromIntegral
instance Field Int32 where
fromField = fromIntegral
toField = fromIntegral
instance Field Int64 where
fromField = fromIntegral
toField = fromIntegral
instance (FiniteBits b, Integral b, BitOffset a) => Field (BitSet b a) where
fromField = fromIntegral . BitSet.toBits
toField = BitSet.fromBits . fromIntegral
instance (Integral b, CEnum a) => Field (EnumField b a) where
fromField = fromCEnum . fromEnumField
toField = toEnumField . toCEnum
extractField :: forall (name :: Symbol) fields b .
( KnownNat (Offset name fields)
, KnownNat (Size name fields)
, WholeSize fields ~ BitSize b
, Bits b, Integral b
, Field (Output name fields)
) => BitFields b fields -> Output name fields
{-# INLINABLE extractField #-}
extractField = extractField' @name
extractField' :: forall (name :: Symbol) fields b .
( KnownNat (Offset name fields)
, KnownNat (Size name fields)
, Bits b, Integral b
, Field (Output name fields)
) => BitFields b fields -> Output name fields
{-# INLINABLE extractField' #-}
extractField' (BitFields w) = toField ((w `shiftR` off) .&. ((1 `shiftL` sz) - 1))
where
off = natValue @(Offset name fields)
sz = natValue @(Size name fields)
updateField :: forall name fields b .
( KnownNat (Offset name fields)
, KnownNat (Size name fields)
, WholeSize fields ~ BitSize b
, Bits b, Integral b
, Field (Output name fields)
) => Output name fields -> BitFields b fields -> BitFields b fields
{-# INLINABLE updateField #-}
updateField = updateField' @name
updateField' :: forall name fields b .
( KnownNat (Offset name fields)
, KnownNat (Size name fields)
, Bits b, Integral b
, Field (Output name fields)
) => Output name fields -> BitFields b fields -> BitFields b fields
{-# INLINABLE updateField' #-}
updateField' value (BitFields w) = BitFields $ ((fromField value `shiftL` off) .&. mask') .|. (w .&. complement mask')
where
off = natValue @(Offset name fields)
sz = natValue @(Size name fields)
mask' = ((1 `shiftL` sz) - 1) `shiftL` off
withField :: forall name fields b f .
( KnownNat (Offset name fields)
, KnownNat (Size name fields)
, WholeSize fields ~ BitSize b
, Bits b, Integral b
, f ~ Output name fields
, Field f
) => (f -> f) -> BitFields b fields -> BitFields b fields
{-# INLINABLE withField #-}
withField = withField' @name
withField' :: forall (name :: Symbol) fields b f .
( KnownNat (Offset name fields)
, KnownNat (Size name fields)
, Bits b, Integral b
, f ~ Output name fields
, Field f
) => (f -> f) -> BitFields b fields -> BitFields b fields
{-# INLINABLE withField' #-}
withField' f bs = updateField' @name (f v) bs
where
v = extractField' @name bs
data Extract = Extract
data Name = Name
instance forall name bs b l l2 i (n :: Nat) s r w .
( bs ~ BitFields w l
, b ~ BitField n name s
, i ~ (bs, HList l2)
, r ~ (bs, HList (Output name l ': l2))
, BitSize w ~ WholeSize l
, Integral w, Bits w
, KnownNat (Offset name l)
, KnownNat (Size name l)
, Field (Output name l)
) => Apply Extract (b, i) r where
apply _ (_, (bs,xs)) =
(bs, HCons (extractField @name bs) xs)
instance forall name bs b l l2 i (n :: Nat) s r w .
( bs ~ BitFields w l
, b ~ BitField n name s
, i ~ HList l2
, r ~ HList (String ': l2)
, KnownSymbol name
) => Apply Name (b, i) r where
apply _ (_, xs) = HCons (symbolValue @name) xs
fieldValues :: forall l l2 w bs .
( bs ~ BitFields w l
, HFoldr' Extract (bs, HList '[]) l (bs, HList l2)
) => bs -> HList l2
fieldValues bs = snd res
where
res :: (bs, HList l2)
res = hFoldr' Extract ((bs, HNil) :: (bs, HList '[])) (undefined :: HList l)
fieldNames :: forall l l2 w bs .
( bs ~ BitFields w l
, HFoldr' Name (HList '[]) l (HList l2)
) => bs -> HList l2
fieldNames _ = hFoldr' Name (HNil :: HList '[]) (undefined :: HList l)
matchFields :: forall l l2 w bs t .
( bs ~ BitFields w l
, HFoldr' Extract (bs, HList '[]) l (bs, HList l2)
, HTuple l2
, t ~ Tuple l2
) => bs -> t
matchFields = hToTuple @l2 . fieldValues
matchNamedFields ::forall lt lv ln lnv w bs t .
( bs ~ BitFields w lt
, HFoldr' Extract (bs, HList '[]) lt (bs, HList lv)
, HFoldr' Name (HList '[]) lt (HList ln)
, HZipList ln lv lnv
, HTuple lnv
, t ~ Tuple lnv
) => bs -> t
matchNamedFields = hToTuple @lnv . matchNamedFields'
matchNamedFields' ::forall lt lv ln lnv w bs .
( bs ~ BitFields w lt
, HFoldr' Extract (bs, HList '[]) lt (bs, HList lv)
, HFoldr' Name (HList '[]) lt (HList ln)
, HZipList ln lv lnv
) => bs -> HList lnv
matchNamedFields' bs = hZipList names values
where
names = fieldNames bs
values = fieldValues bs
instance forall lt ln lnv w bs.
( bs ~ BitFields w lt
, ln ~ Replicate (Length lt) String
, HFoldr' Extract (bs, HList '[]) lt (bs, HList (BitFieldTypes lt))
, HFoldr' Name (HList '[]) lt (HList ln)
, HZipList ln (BitFieldTypes lt) lnv
, Show (HList lnv)
) => Show (BitFields w lt) where
show bs = show (matchNamedFields' bs :: HList lnv)
instance forall lt lt2 w bs.
( bs ~ BitFields w lt
, HFoldr' Extract (bs, HList '[]) lt (bs, HList lt2)
, Eq (HList lt2)
, lt2 ~ BitFieldTypes lt
) => Eq (BitFields w lt) where
(==) x y = x' == y'
where
x' :: HList lt2
x' = fieldValues x
y' :: HList lt2
y' = fieldValues y