|
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 |
-}
|