binrep-0.2.0: src/Binrep/BLen/Internal/AsBLen.hs
module Binrep.BLen.Internal.AsBLen where
import GHC.Natural ( minusNaturalMaybe )
import GHC.Num.Natural
import GHC.Exts
import Binrep.Util ( posIntToNat )
-- | Helper definitions for using the given type to store byte lengths.
--
-- Byte lengths must be non-negative. Thus, the ideal representation is a
-- 'Natural'. However, most underlying types that we use ('B.ByteString', lists)
-- store their length in 'Int's. By similarly storing an 'Int' ourselves, we
-- could potentially improve performance.
--
-- I like both options, and don't want to give up either. So we provide helpers
-- via a typeclass so that the user doesn't ever have to think about the
-- underlying type.
--
-- For simplicity, documentation may consider 'a' to be an "unsigned" type. For
-- example, underflow refers to a negative 'a' result.
class AsBLen a where
-- | Safe blen subtraction, returning 'Nothing' for negative results.
--
-- Regular subtraction should only be used when you have a guarantee that it
-- won't underflow.
safeBLenSub :: a -> a -> Maybe a
-- | Convert some 'Int' @i@ where @i >= 0@ to a blen.
--
-- This is intended for wrapping the output of 'length' functions.
posIntToBLen :: Int -> a
-- | Convert some 'Word#' @w@ where @w <= maxBound @a@ to a blen.
wordToBLen# :: Word# -> a
-- | Convert some 'Natural' @n@ where @n <= maxBound @a@ to a blen.
natToBLen :: Natural -> a
instance AsBLen Int where
safeBLenSub x y = if z >= 0 then Just z else Nothing where z = x - y
{-# INLINE safeBLenSub #-}
posIntToBLen = id
{-# INLINE posIntToBLen #-}
natToBLen = \case
NS w# -> wordToBLen# w#
NB _ -> error "TODO natural too large"
{-# INLINE natToBLen #-}
wordToBLen# w# = I# (word2Int# w#)
{-# INLINE wordToBLen# #-}
instance AsBLen Natural where
safeBLenSub = minusNaturalMaybe
{-# INLINE safeBLenSub #-}
posIntToBLen = posIntToNat
{-# INLINE posIntToBLen #-}
wordToBLen# = NS
{-# INLINE wordToBLen# #-}
natToBLen = id
{-# INLINE natToBLen #-}