packages feed

cardano-crypto-1.3.0: src/Crypto/Math/Bytes.hs

-- copied & adapted from cryptic
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE ConstraintKinds     #-}
{-# LANGUAGE TypeOperators       #-}
{-# LANGUAGE Rank2Types          #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE ViewPatterns        #-}
module Crypto.Math.Bytes
    ( Bytes
    , Endian(..)
    , pack
    , packSome
    , unpack
    , fromBits
    , toBits
    , append
    , take
    , drop
    , splitHalf
    , trace
    ) where

import           Data.Proxy
import           Data.Word
import           Data.List (foldl')
import           GHC.Natural
import           GHC.TypeLits
import           Crypto.Math.NatMath
import           Data.Bits (shiftL)
import           Crypto.Math.Bits (FBits(..))
import           Prelude hiding (take, drop)
import qualified Prelude
import qualified Debug.Trace as Trace

newtype Bytes (n :: Nat) = Bytes { unpack :: [Word8] }
    deriving (Show,Eq)

data Endian = LittleEndian | BigEndian
    deriving (Show,Eq)

pack :: forall n . KnownNat n => [Word8] -> Bytes n
pack l
    | n == len = Bytes l
    | otherwise = error "packing failed: length not as expected"
  where
    len = length l
    n = fromIntegral $ natVal (Proxy :: Proxy n)

packSome :: (forall n . KnownNat n => Bytes n -> a) -> [Word8] -> a
packSome f l = case someNatVal (fromIntegral len) of
                    Nothing          -> error "impossible"
                    Just (SomeNat (_ :: Proxy n)) -> f (pack l :: Bytes n)
  where len = length l

fixupBytes :: Endian -> [Word8] -> [Word8]
fixupBytes LittleEndian = reverse
fixupBytes BigEndian    = id

trace :: String -> Bytes n -> Bytes n
trace cmd b@(Bytes l) = Trace.trace (cmd ++ ": " ++ concatMap toHex l) b
  where
    toHex w = let (x,y) = w `divMod` 16 in [hex x, hex y]
    hex i | i < 10    = toEnum (fromEnum '0' + fromIntegral i)
          | otherwise = toEnum (fromEnum 'a' + fromIntegral (i-10))

-- | transform bytes into bits with a specific endianness
toBits :: Endian -> Bytes n -> FBits (n * 8)
toBits endian (Bytes l) = FBits $
    foldl' (\acc i -> (acc `shiftL` 8) + fromIntegral i) 0 (fixupBytes endian l)

-- | transform bits into bytes with a specific endianness
fromBits :: forall n . KnownNat n => Endian -> FBits n -> Bytes (Div8 n)
fromBits endian (unFBits -> allBits) = Bytes $ loop [] (0 :: Word) allBits
  where
    n = natVal (Proxy :: Proxy n)
    loop acc i nat
        | fromIntegral i > n  = error "binFromFBits over"
        | fromIntegral i == n = fixupBytes endian acc
        | otherwise           =
            let (nat', b) = divMod8 nat
             in loop (b:acc) (i+8) nat'

    divMod8 :: Natural -> (Natural, Word8)
    divMod8 i = let (q,m) = i `divMod` 256 in (q,fromIntegral m)


splitHalf :: forall m n . (KnownNat n, (n * 2) ~ m) => Bytes m -> (Bytes n, Bytes n)
splitHalf (Bytes l) = (Bytes l1, Bytes l2)
  where
    (l1, l2) = splitAt n l
    n = fromIntegral $ natVal (Proxy :: Proxy n)

append :: forall m n r . ((m + n) ~ r)
       => Bytes n -> Bytes m -> Bytes r
append (Bytes a) (Bytes b) = Bytes (a ++ b)

take :: forall n m .(KnownNat n, n <= m) => Bytes m -> Bytes n
take (Bytes l) = Bytes $ Prelude.take (fromIntegral $ natVal (Proxy :: Proxy n)) l

drop :: forall n m . (KnownNat m, KnownNat n, n <= m) => Bytes m -> Bytes n
drop (Bytes l) = Bytes $ Prelude.drop (fromIntegral diff) l
  where diff = natVal (Proxy :: Proxy m) - natVal (Proxy :: Proxy n)