Blame Data/ByteArray/Bytes.hs

Packit c1c4f9
-- |
Packit c1c4f9
-- Module      : Data.ByteArray.Bytes
Packit c1c4f9
-- License     : BSD-style
Packit c1c4f9
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit c1c4f9
-- Stability   : stable
Packit c1c4f9
-- Portability : Good
Packit c1c4f9
--
Packit c1c4f9
-- Simple and efficient byte array types
Packit c1c4f9
--
Packit c1c4f9
{-# LANGUAGE CPP #-}
Packit c1c4f9
{-# LANGUAGE BangPatterns #-}
Packit c1c4f9
{-# LANGUAGE MagicHash #-}
Packit c1c4f9
{-# LANGUAGE UnboxedTuples #-}
Packit c1c4f9
module Data.ByteArray.Bytes
Packit c1c4f9
    ( Bytes
Packit c1c4f9
    ) where
Packit c1c4f9
Packit c1c4f9
import           GHC.Types
Packit c1c4f9
import           GHC.Prim
Packit c1c4f9
import           GHC.Ptr
Packit c1c4f9
#if MIN_VERSION_base(4,9,0)
Packit c1c4f9
import           Data.Semigroup
Packit c1c4f9
import           Data.Foldable (toList)
Packit c1c4f9
#else
Packit c1c4f9
import           Data.Monoid
Packit c1c4f9
#endif
Packit c1c4f9
import           Data.Memory.PtrMethods
Packit c1c4f9
import           Data.Memory.Internal.Imports
Packit c1c4f9
import           Data.Memory.Internal.CompatPrim
Packit c1c4f9
import           Data.Memory.Internal.Compat      (unsafeDoIO)
Packit c1c4f9
import           Data.ByteArray.Types
Packit c1c4f9
Packit c1c4f9
-- | Simplest Byte Array
Packit c1c4f9
data Bytes = Bytes (MutableByteArray# RealWorld)
Packit c1c4f9
Packit c1c4f9
instance Show Bytes where
Packit c1c4f9
    showsPrec p b r = showsPrec p (bytesUnpackChars b []) r
Packit c1c4f9
instance Eq Bytes where
Packit c1c4f9
    (==) = bytesEq
Packit c1c4f9
instance Ord Bytes where
Packit c1c4f9
    compare = bytesCompare
Packit c1c4f9
#if MIN_VERSION_base(4,9,0)
Packit c1c4f9
instance Semigroup Bytes where
Packit c1c4f9
    b1 <> b2      = unsafeDoIO $ bytesAppend b1 b2
Packit c1c4f9
    sconcat       = unsafeDoIO . bytesConcat . toList
Packit c1c4f9
#endif
Packit c1c4f9
instance Monoid Bytes where
Packit c1c4f9
    mempty        = unsafeDoIO (newBytes 0)
Packit c1c4f9
#if !(MIN_VERSION_base(4,11,0))
Packit c1c4f9
    mappend b1 b2 = unsafeDoIO $ bytesAppend b1 b2
Packit c1c4f9
    mconcat       = unsafeDoIO . bytesConcat
Packit c1c4f9
#endif
Packit c1c4f9
instance NFData Bytes where
Packit c1c4f9
    rnf b = b `seq` ()
Packit c1c4f9
instance ByteArrayAccess Bytes where
Packit c1c4f9
    length        = bytesLength
Packit c1c4f9
    withByteArray = withBytes
Packit c1c4f9
instance ByteArray Bytes where
Packit c1c4f9
    allocRet = bytesAllocRet
Packit c1c4f9
Packit c1c4f9
------------------------------------------------------------------------
Packit c1c4f9
newBytes :: Int -> IO Bytes
Packit c1c4f9
newBytes (I# sz)
Packit c1c4f9
    | booleanPrim (sz <# 0#) = error "Bytes: size must be >= 0"
Packit c1c4f9
    | otherwise              = IO $ \s ->
Packit c1c4f9
        case newAlignedPinnedByteArray# sz 8# s of
Packit c1c4f9
            (# s', mbarr #) -> (# s', Bytes mbarr #)
Packit c1c4f9
Packit c1c4f9
touchBytes :: Bytes -> IO ()
Packit c1c4f9
touchBytes (Bytes mba) = IO $ \s -> case touch# mba s of s' -> (# s', () #)
Packit c1c4f9
{-# INLINE touchBytes #-}
Packit c1c4f9
Packit c1c4f9
sizeofBytes :: Bytes -> Int
Packit c1c4f9
sizeofBytes (Bytes mba) = I# (sizeofMutableByteArray# mba)
Packit c1c4f9
{-# INLINE sizeofBytes #-}
Packit c1c4f9
Packit c1c4f9
withPtr :: Bytes -> (Ptr p -> IO a) -> IO a
Packit c1c4f9
withPtr b@(Bytes mba) f = do
Packit c1c4f9
    a <- f (Ptr (byteArrayContents# (unsafeCoerce# mba)))
Packit c1c4f9
    touchBytes b
Packit c1c4f9
    return a
Packit c1c4f9
------------------------------------------------------------------------
Packit c1c4f9
Packit c1c4f9
bytesAlloc :: Int -> (Ptr p -> IO ()) -> IO Bytes
Packit c1c4f9
bytesAlloc sz f = do
Packit c1c4f9
    ba <- newBytes sz
Packit c1c4f9
    withPtr ba f
Packit c1c4f9
    return ba
Packit c1c4f9
Packit c1c4f9
bytesConcat :: [Bytes] -> IO Bytes
Packit c1c4f9
bytesConcat l = bytesAlloc retLen (copy l)
Packit c1c4f9
  where
Packit c1c4f9
    !retLen = sum $ map bytesLength l
Packit c1c4f9
Packit c1c4f9
    copy []     _   = return ()
Packit c1c4f9
    copy (x:xs) dst = do
Packit c1c4f9
        withPtr x $ \src -> memCopy dst src chunkLen
Packit c1c4f9
        copy xs (dst `plusPtr` chunkLen)
Packit c1c4f9
      where
Packit c1c4f9
        !chunkLen = bytesLength x
Packit c1c4f9
Packit c1c4f9
bytesAppend :: Bytes -> Bytes -> IO Bytes
Packit c1c4f9
bytesAppend b1 b2 = bytesAlloc retLen $ \dst -> do
Packit c1c4f9
    withPtr b1 $ \s1 -> memCopy dst                  s1 len1
Packit c1c4f9
    withPtr b2 $ \s2 -> memCopy (dst `plusPtr` len1) s2 len2
Packit c1c4f9
  where
Packit c1c4f9
    !len1   = bytesLength b1
Packit c1c4f9
    !len2   = bytesLength b2
Packit c1c4f9
    !retLen = len1 + len2
Packit c1c4f9
Packit c1c4f9
bytesAllocRet :: Int -> (Ptr p -> IO a) -> IO (a, Bytes)
Packit c1c4f9
bytesAllocRet sz f = do
Packit c1c4f9
    ba <- newBytes sz
Packit c1c4f9
    r <- withPtr ba f
Packit c1c4f9
    return (r, ba)
Packit c1c4f9
Packit c1c4f9
bytesLength :: Bytes -> Int
Packit c1c4f9
bytesLength = sizeofBytes
Packit c1c4f9
{-# LANGUAGE bytesLength #-}
Packit c1c4f9
Packit c1c4f9
withBytes :: Bytes -> (Ptr p -> IO a) -> IO a
Packit c1c4f9
withBytes = withPtr
Packit c1c4f9
Packit c1c4f9
bytesEq :: Bytes -> Bytes -> Bool
Packit c1c4f9
bytesEq b1@(Bytes m1) b2@(Bytes m2)
Packit c1c4f9
    | l1 /= l2  = False
Packit c1c4f9
    | otherwise = unsafeDoIO $ IO $ \s -> loop 0# s
Packit c1c4f9
  where
Packit c1c4f9
    !l1@(I# len) = bytesLength b1
Packit c1c4f9
    !l2          = bytesLength b2
Packit c1c4f9
Packit c1c4f9
    loop i s
Packit c1c4f9
        | booleanPrim (i ==# len) = (# s, True #)
Packit c1c4f9
        | otherwise               =
Packit c1c4f9
            case readWord8Array# m1 i s of
Packit c1c4f9
                (# s', e1 #) -> case readWord8Array# m2 i s' of
Packit c1c4f9
                    (# s'', e2 #) ->
Packit c1c4f9
                        if booleanPrim (eqWord# e1 e2)
Packit c1c4f9
                            then loop (i +# 1#) s''
Packit c1c4f9
                            else (# s'', False #)
Packit c1c4f9
    {-# INLINE loop #-}
Packit c1c4f9
Packit c1c4f9
bytesCompare :: Bytes -> Bytes -> Ordering
Packit c1c4f9
bytesCompare b1@(Bytes m1) b2@(Bytes m2) = unsafeDoIO $ IO $ \s -> loop 0# s
Packit c1c4f9
  where
Packit c1c4f9
    !l1       = bytesLength b1
Packit c1c4f9
    !l2       = bytesLength b2
Packit c1c4f9
    !(I# len) = min l1 l2
Packit c1c4f9
Packit c1c4f9
    loop i s1
Packit c1c4f9
        | booleanPrim (i ==# len) =
Packit c1c4f9
            if l1 == l2
Packit c1c4f9
                then (# s1, EQ #)
Packit c1c4f9
                else if l1 > l2 then (# s1, GT #)
Packit c1c4f9
                                else (# s1, LT #)
Packit c1c4f9
        | otherwise               =
Packit c1c4f9
            case readWord8Array# m1 i s1 of
Packit c1c4f9
                (# s2, e1 #) -> case readWord8Array# m2 i s2 of
Packit c1c4f9
                    (# s3, e2 #) ->
Packit c1c4f9
                        if booleanPrim (eqWord# e1 e2)
Packit c1c4f9
                            then loop (i +# 1#) s3
Packit c1c4f9
                            else if booleanPrim (ltWord# e1 e2) then (# s3, LT #)
Packit c1c4f9
                                                                else (# s3, GT #)
Packit c1c4f9
Packit c1c4f9
bytesUnpackChars :: Bytes -> String -> String
Packit c1c4f9
bytesUnpackChars (Bytes mba) xs = chunkLoop 0#
Packit c1c4f9
  where
Packit c1c4f9
    !len = sizeofMutableByteArray# mba
Packit c1c4f9
    -- chunk 64 bytes at a time
Packit c1c4f9
    chunkLoop :: Int# -> [Char]
Packit c1c4f9
    chunkLoop idx
Packit c1c4f9
        | booleanPrim (len ==# idx) = []
Packit c1c4f9
        | booleanPrim ((len -# idx) ># 63#) =
Packit c1c4f9
            bytesLoop idx 64# (chunkLoop (idx +# 64#))
Packit c1c4f9
        | otherwise =
Packit c1c4f9
            bytesLoop idx (len -# idx) xs
Packit c1c4f9
Packit c1c4f9
    bytesLoop idx chunkLenM1 paramAcc = unsafeDoIO $
Packit c1c4f9
        loop (idx +# chunkLenM1 -# 1#) paramAcc
Packit c1c4f9
      where loop i acc
Packit c1c4f9
                | booleanPrim (i ==# idx) = do
Packit c1c4f9
                    c <- rChar i
Packit c1c4f9
                    return (c : acc)
Packit c1c4f9
                | otherwise = do
Packit c1c4f9
                    c <- rChar i
Packit c1c4f9
                    loop (i -# 1#) (c : acc)
Packit c1c4f9
Packit c1c4f9
    rChar :: Int# -> IO Char
Packit c1c4f9
    rChar idx = IO $ \s ->
Packit c1c4f9
        case readWord8Array# mba idx s of
Packit c1c4f9
            (# s2, w #) -> (# s2, C# (chr# (word2Int# w)) #)
Packit c1c4f9
Packit c1c4f9
{-
Packit c1c4f9
bytesShowHex :: Bytes -> String
Packit c1c4f9
bytesShowHex b = showHexadecimal (withPtr b) (bytesLength b)
Packit c1c4f9
{-# NOINLINE bytesShowHex #-}
Packit c1c4f9
-}