packages feed

binrep-0.2.0: src/Binrep/Type/LenPfx.hs

-- TODO cleanup proxy usage (can we be faster via unboxed @Proxy#@ s ?)

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}

module Binrep.Type.LenPfx where

import Binrep
import Strongweak
import Data.Either.Validation
import Binrep.Type.Vector ( getVector )
import Binrep.Type.Common ( Endianness )
import Binrep.Type.Int
import Binrep.Util ( natVal'' )
import Data.Vector.Sized ( Vector )
import Data.Vector.Sized qualified as V
import GHC.TypeNats
import GHC.TypeLits ( OrderingI(..) )
import Data.Proxy ( Proxy(..) )

import GHC.Generics
import Data.Typeable ( Typeable )

-- | Holy shit - no need to do a smart constructor, it's simply impossible to
--   instantiate invalid values of this type!
data LenPfx (size :: ISize) (end :: Endianness) a =
    forall n. (KnownNat n, n <= IMax 'U size) => LenPfx { unLenPfx :: Vector n a }

-- uhhhhhhhhhh i dunno. TODO
instance Generic (LenPfx size end a) where
    type Rep (LenPfx size end a) = Rec0 (LenPfx size end a)
    from = K1
    to = unK1

instance Eq a => Eq (LenPfx size end a) where
    (LenPfx a) == (LenPfx b) = vsEq a b

-- TODO
instance Show a => Show (LenPfx size end a) where
    show (LenPfx a) = "LenPfx ("<>show a<>")"

vsEq :: forall a n m. (Eq a, KnownNat n, KnownNat m) => Vector n a -> Vector m a -> Bool
vsEq vn vm =
    if   natVal'' @n == natVal'' @m
    then V.toList vn == V.toList vm
    else False

instance Weaken (LenPfx size end a) where
    type Weak (LenPfx size end a) = [a]
    weaken (LenPfx v) = V.toList v

instance (KnownNat (MaxBound (IRep 'U size)), Show a, Typeable a, Typeable size, Typeable end)
  => Strengthen (LenPfx size end a) where
    strengthen l = case lenPfxFromList l of
                     Nothing -> strengthenFailBase l "TODO doesn't fit"
                     Just v  -> Success v

asLenPfx
    :: forall size end n a irep
    .  (irep ~ IRep 'U size, KnownNat n, KnownNat (MaxBound irep))
    => Vector n a -> Maybe (LenPfx size end a)
asLenPfx v =
    case cmpNat (Proxy :: Proxy n) (Proxy :: Proxy (MaxBound (IRep 'U size))) of
      LTI -> Just $ LenPfx v
      EQI -> Just $ LenPfx v
      GTI -> Nothing

lenPfxFromList
    :: forall size end a irep
    .  (irep ~ IRep 'U size, KnownNat (MaxBound irep))
    => [a] -> Maybe (LenPfx size end a)
lenPfxFromList l = V.withSizedList l asLenPfx

instance (BLen a, itype ~ I 'U size end, KnownNat (CBLen itype))
  => BLen (LenPfx size end a) where
    blen (LenPfx v) = cblen @itype + blen v

instance (itype ~ I 'U size end, irep ~ IRep 'U size, Put a, Put itype, Num irep)
  => Put (LenPfx size end a) where
    put (LenPfx v) = put @itype (fromIntegral (vnatVal v)) <> put v
      where
        vnatVal :: forall n x. KnownNat n => Vector n x -> Natural
        vnatVal _ = natVal'' @n

lenPfxSize :: Num (IRep 'U size) => LenPfx size end a -> I 'U size end
lenPfxSize (LenPfx v) = fromIntegral (vnatVal v)
  where
    vnatVal :: forall n x. KnownNat n => Vector n x -> Natural
    vnatVal _ = natVal'' @n

instance (itype ~ I 'U size end, irep ~ IRep 'U size, Get itype, Integral irep, Get a, KnownNat (MaxBound irep))
  => Get (LenPfx size end a) where
    get = getLenPfx get

getLenPfx
    :: forall size end a itype irep
    .  (itype ~ I 'U size end, irep ~ IRep 'U size, Get itype, Integral irep, KnownNat (MaxBound irep))
    => Getter a -> Getter (LenPfx size end a)
getLenPfx g = do
    len <- get @itype
    case someNatVal (fromIntegral len) of
      SomeNat (Proxy :: Proxy n) -> do
        x <- getVector @n g
        -- TODO we actually know that @n <= MaxBound irep@ before doing this
        -- because @len <= maxBound (_ :: irep)@ but that's hard to prove to
        -- GHC without lots of refactoring. This is good enough.
        case cmpNat (Proxy :: Proxy n) (Proxy :: Proxy (MaxBound irep)) of
          GTI -> error "impossible"
          LTI -> return $ LenPfx x
          EQI -> return $ LenPfx x