packed-0.1.0: src/Packed/Bytes/Small.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC
-Weverything
-fno-warn-unsafe
-fno-warn-implicit-prelude
-fno-warn-missing-import-lists
-O2
#-}
module Packed.Bytes.Small
( ByteArray(..)
, empty
, pack
, unpack
, singleton
, append
, replicate
, length
, findByte
, foldl'
, take
, cons
, uncons
, reverse
-- * Zip
, zipAnd
, zipOr
, zipXor
-- * Characters
, isAscii
-- * Unsafe
, unsafeIndex
) where
import Prelude hiding (replicate,length,take,reverse)
import Control.Monad.Primitive (primitive_)
import Control.Monad.ST (runST,ST)
import Data.Primitive.ByteArray (ByteArray(..),MutableByteArray(..))
import GHC.Exts (setByteArray#,word2Int#,byteSwap#)
import GHC.Int (Int(I#))
import GHC.Word (Word8(W8#),Word(W#))
import qualified GHC.OldList as L
import qualified Packed.Bytes.Window as BAW
import qualified Data.Primitive as PM
singleton :: Word8 -> ByteArray
singleton w = runST $ do
marr <- PM.newByteArray 1
PM.writeByteArray marr 0 w
PM.unsafeFreezeByteArray marr
pack :: [Word8] -> ByteArray
pack ws0 = runST $ do
marr <- PM.newByteArray (L.length ws0)
let go [] !_ = return ()
go (w : ws) !ix = writeByteArrayWord8 marr ix w >> go ws (ix + 1)
go ws0 0
PM.unsafeFreezeByteArray marr
unpack :: ByteArray -> [Word8]
unpack arr = go 0 where
go :: Int -> [Word8]
go !ix = if ix < length arr
then unsafeIndex arr ix : go (ix + 1)
else []
append :: ByteArray -> ByteArray -> ByteArray
append !a !b = do
let !lenA = length a
!lenB = length b
!len = lenA + lenB
in runST $ do
!marr <- PM.newByteArray len
PM.copyByteArray marr 0 a 0 lenA
PM.copyByteArray marr lenA b 0 lenB
PM.unsafeFreezeByteArray marr
cons :: Word8 -> ByteArray -> ByteArray
cons w arr = runST $ do
let !lenArr = length arr
marr <- PM.newByteArray (lenArr + 1)
PM.writeByteArray marr 0 w
PM.copyByteArray marr 1 arr 0 lenArr
PM.unsafeFreezeByteArray marr
uncons :: ByteArray -> Maybe (Word8, ByteArray)
uncons arr0 = if lenArr > 0
then
let !arr1 = runST $ do
marr <- PM.newByteArray (lenArr - 1)
PM.copyByteArray marr 0 arr0 1 (lenArr - 1)
PM.unsafeFreezeByteArray marr
w :: Word8
!w = PM.indexByteArray arr0 0
in Just (w,arr1)
else Nothing
where
!lenArr = length arr0
replicate :: Int -> Word8 -> ByteArray
replicate len@(I# len#) (W8# w#) = runST $ do
marr@(MutableByteArray marr#) <- PM.newByteArray len
primitive_ (setByteArray# marr# 0# len# (word2Int# w#))
PM.unsafeFreezeByteArray marr
length :: ByteArray -> Int
length = PM.sizeofByteArray
findByte :: Word8 -> ByteArray -> Maybe Int
findByte !w !arr = BAW.findByte 0 (length arr) w arr
foldl' :: (a -> Word8 -> a) -> a -> ByteArray -> a
foldl' f !acc0 !arr = BAW.foldl' 0 (length arr) f acc0 arr
take :: Int -> ByteArray -> ByteArray
take !n !arr = if n < length arr
then runST $ do
marr <- PM.newByteArray n
PM.copyByteArray marr 0 arr 0 n
PM.unsafeFreezeByteArray marr
else empty
empty :: ByteArray
empty = runST (PM.newByteArray 0 >>= PM.unsafeFreezeByteArray)
-- | Reverse the bytes in a byte array. This operation is eight
-- times faster if the length of the byte array divides eight
-- evenly.
reverse :: ByteArray -> ByteArray
reverse !arr = runST $ do
marr <- PM.newByteArray (length arr)
let !(!quotient,!remainder) = quotRem (length arr) (PM.sizeOf (undefined :: Word))
if remainder == 0
then goFast 0 quotient marr
else goSlow 0 (length arr) marr
PM.unsafeFreezeByteArray marr
where
goFast :: forall s. Int -> Int -> MutableByteArray s -> ST s ()
goFast !ix !len !marr = if ix < len
then do
let !w = unsafeIndexWord arr ix
writeByteArrayWord marr (len - ix) (byteSwap w)
goFast (ix + 1) len marr
else return ()
goSlow :: forall s. Int -> Int -> MutableByteArray s -> ST s ()
goSlow !ix !len !marr = if ix < len
then do
let !w = unsafeIndex arr ix
writeByteArrayWord8 marr (len - ix) w
goSlow (ix + 1) len marr
else return ()
byteSwap :: Word -> Word
byteSwap (W# w) = W# (byteSwap# w)
writeByteArrayWord :: MutableByteArray s -> Int -> Word -> ST s ()
writeByteArrayWord = PM.writeByteArray
writeByteArrayWord8 :: MutableByteArray s -> Int -> Word8 -> ST s ()
writeByteArrayWord8 = PM.writeByteArray
-- this is only used internally
unsafeIndexWord :: ByteArray -> Int -> Word
unsafeIndexWord = PM.indexByteArray
-- | Does not check to see if the index is in bounds.
unsafeIndex :: ByteArray -> Int -> Word8
unsafeIndex = PM.indexByteArray
zipAnd :: ByteArray -> ByteArray -> ByteArray
zipAnd x y = BAW.zipAnd 0 (length x) 0 (length y) x y
zipOr :: ByteArray -> ByteArray -> ByteArray
zipOr x y = BAW.zipOr 0 (length x) 0 (length y) x y
zipXor :: ByteArray -> ByteArray -> ByteArray
zipXor x y = BAW.zipXor 0 (length x) 0 (length y) x y
isAscii :: ByteArray -> Bool
isAscii x = BAW.isAscii 0 (length x) x