{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
module Haskus.Binary.Buffer
( Buffer (..)
, withBufferPtr
, bufferSize
, isBufferEmpty
, emptyBuffer
, bufferZero
, bufferMap
, bufferReverse
, bufferDrop
, bufferTail
, bufferAppend
, bufferCons
, bufferSnoc
, bufferInit
, bufferSplitOn
, bufferHead
, bufferIndex
, bufferTake
, bufferTakeWhile
, bufferTakeAtMost
, bufferZipWith
, bufferDup
, bufferPeekStorable
, bufferPeekStorableAt
, bufferPopStorable
, bufferPoke
, bufferPackByteString
, bufferPackByteList
, bufferPackStorable
, bufferPackStorableList
, bufferPackPtr
, bufferUnpackByteList
, bufferUnpackByteString
, bufferUnsafeDrop
, bufferUnsafeTake
, bufferUnsafeTail
, bufferUnsafeHead
, bufferUnsafeLast
, bufferUnsafeInit
, bufferUnsafeIndex
, bufferUnsafeMapMemory
, bufferUnsafeUsePtr
, bufferUnsafePackPtr
, bufferReadFile
, bufferWriteFile
)
where
import Prelude hiding (length)
import System.IO.Unsafe
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BS
import Foreign.Ptr
import Foreign.Marshal.Alloc (mallocBytes)
import Haskus.Number.Word
import Haskus.Binary.Storable
import Haskus.Binary.Bits.Helper
import Haskus.Binary.Bits.Bitwise
import Haskus.Binary.Bits.Index
import Haskus.Binary.Bits.Shift
import Haskus.Memory.Utils (memCopy,memSet)
import Haskus.Utils.List as List
import Haskus.Utils.Flow
newtype Buffer = Buffer ByteString deriving (Eq,Ord)
instance Show Buffer where
show b = concatMap bToHex (bufferUnpackByteList b)
where
bToHex x = toHex (x `shiftR` 4) ++ toHex (x .&. 0x0F)
toHex 0xA = "A"
toHex 0xB = "B"
toHex 0xC = "C"
toHex 0xD = "D"
toHex 0xE = "E"
toHex 0xF = "F"
toHex x = show x
instance Bitwise Buffer where
(.&.) = bufferZipWith (.&.)
(.|.) = bufferZipWith (.|.)
xor = bufferZipWith xor
instance IndexableBits Buffer where
bit i = bufferPackByteList
(bit r : List.replicate (fromIntegral n) 0)
where
n = byteOffset i
r = bitOffset i
testBit b i = testBit p r
where
p = bufferIndex b (bufferSize b - n)
n = byteOffset i
r = bitOffset i
setBit = error "Can't set Buffer bit"
clearBit = error "Can't clear Buffer bit"
popCount b = sum (fmap popCount (bufferUnpackByteList b))
bufferDup :: Buffer -> IO Buffer
bufferDup b = withBufferPtr b $ bufferPackPtr (bufferSize b)
bufferZero :: Word -> Buffer
bufferZero n = unsafePerformIO $ do
p <- mallocBytes (fromIntegral n)
memSet p (fromIntegral n) 0
bufferUnsafePackPtr n p
bufferZipWith :: (Word8 -> Word8 -> Word8) -> Buffer -> Buffer -> Buffer
bufferZipWith f a b
| bufferSize a /= bufferSize b = error "Non matching buffer sizes"
| otherwise = unsafePerformIO $ do
let sz = fromIntegral (bufferSize a)
pc <- mallocBytes sz
withBufferPtr a $ \pa ->
withBufferPtr b $ \pb ->
forM_ [0..fromIntegral sz-1] $ \off -> do
v <- f <$> peekByteOff pa off
<*> peekByteOff pb off
pokeByteOff pc off (v :: Word8)
bufferUnsafePackPtr (bufferSize a) pc
withBufferPtr :: Buffer -> (Ptr b -> IO a) -> IO a
withBufferPtr (Buffer bs) f = BS.unsafeUseAsCString bs (f . castPtr)
isBufferEmpty :: Buffer -> Bool
isBufferEmpty (Buffer bs) = BS.null bs
emptyBuffer :: Buffer
emptyBuffer = Buffer BS.empty
bufferSize :: Buffer -> Word
bufferSize (Buffer bs) =
if s < 0
then error "ByteString with size < 0"
else fromIntegral s
where
s = BS.length bs
bufferPeekStorable :: forall a. Storable a => Buffer -> a
bufferPeekStorable = snd . bufferPopStorable
bufferPeekStorableAt :: forall a.
( Storable a
)
=> Buffer -> Word -> a
bufferPeekStorableAt b n
| n + sizeOfT' @a > bufferSize b = error "Invalid buffer index"
| otherwise = unsafePerformIO $ withBufferPtr b $ \p ->
peekByteOff p (fromIntegral n)
bufferPopStorable :: forall a. Storable a => Buffer -> (Buffer,a)
bufferPopStorable buf
| bufferSize buf < sza = error "bufferRead: out of bounds"
| otherwise = unsafePerformIO $ do
a <- withBufferPtr buf peek
return (bufferDrop sza buf, a)
where
sza = sizeOfT' @a
bufferPoke :: Ptr a -> Buffer -> IO ()
bufferPoke dest b = bufferUnsafeUsePtr b $ \src sz ->
memCopy dest src (fromIntegral sz)
bufferMap :: (Word8 -> Word8) -> Buffer -> Buffer
bufferMap f (Buffer bs) = Buffer (BS.map f bs)
bufferReverse :: Buffer -> Buffer
bufferReverse (Buffer bs) = Buffer (BS.reverse bs)
bufferDrop :: Word -> Buffer -> Buffer
bufferDrop n (Buffer bs) = Buffer $ BS.drop (fromIntegral n) bs
bufferSplitOn :: Word8 -> Buffer -> [Buffer]
bufferSplitOn n (Buffer bs) = fmap Buffer (BS.split n bs)
bufferTail :: Buffer -> Buffer
bufferTail (Buffer bs) = Buffer $ BS.tail bs
bufferAppend :: Buffer -> Buffer -> Buffer
bufferAppend (Buffer a) (Buffer b) = Buffer $ BS.append a b
bufferCons :: Word8 -> Buffer -> Buffer
bufferCons w (Buffer bs) = Buffer $ BS.cons w bs
bufferSnoc :: Buffer -> Word8 -> Buffer
bufferSnoc (Buffer bs) w = Buffer $ BS.snoc bs w
bufferInit :: Buffer -> Buffer
bufferInit (Buffer bs) = Buffer $ BS.init bs
bufferHead :: Buffer -> Word8
{-# INLINABLE bufferHead #-}
bufferHead (Buffer bs) = BS.head bs
bufferIndex :: Buffer -> Word -> Word8
{-# INLINABLE bufferIndex #-}
bufferIndex (Buffer bs) n = BS.index bs (fromIntegral n)
bufferUnpackByteList :: Buffer -> [Word8]
bufferUnpackByteList (Buffer bs) = BS.unpack bs
bufferUnpackByteString :: Buffer -> ByteString
bufferUnpackByteString (Buffer bs) = bs
bufferTake :: Word -> Buffer -> Buffer
bufferTake n (Buffer bs) = Buffer $ BS.take (fromIntegral n) bs
bufferTakeWhile :: (Word8 -> Bool) -> Buffer -> Buffer
bufferTakeWhile f (Buffer bs) = Buffer $ BS.takeWhile f bs
bufferTakeAtMost :: Word -> Buffer -> Buffer
bufferTakeAtMost n buf
| bufferSize buf < n = buf
| otherwise = bufferTake n buf
bufferPackByteString :: BS.ByteString -> Buffer
bufferPackByteString = Buffer
bufferPackByteList :: [Word8] -> Buffer
bufferPackByteList = Buffer . BS.pack
bufferPackStorable :: forall a. Storable a => a -> Buffer
bufferPackStorable x = Buffer $ unsafePerformIO $ do
p <- malloc
poke p x
BS.unsafePackMallocCStringLen (castPtr p, sizeOfT' @a)
bufferPackStorableList :: forall a. Storable a => [a] -> Buffer
bufferPackStorableList xs = Buffer $ unsafePerformIO $ do
let lxs = length xs
p <- mallocArray lxs
forM_ (xs `zip` [0..]) $ \(x,o) ->
pokeElemOff p o x
BS.unsafePackMallocCStringLen (castPtr p, sizeOfT' @a * fromIntegral lxs)
bufferPackPtr :: MonadIO m => Word -> Ptr () -> m Buffer
bufferPackPtr sz ptr = do
p <- liftIO (mallocBytes (fromIntegral sz))
memCopy p ptr (fromIntegral sz)
bufferUnsafePackPtr sz p
bufferUnsafePackPtr :: MonadIO m => Word -> Ptr a -> m Buffer
bufferUnsafePackPtr sz p =
Buffer <$> liftIO (BS.unsafePackMallocCStringLen (castPtr p, fromIntegral sz))
bufferUnsafeDrop :: Word -> Buffer -> Buffer
bufferUnsafeDrop n (Buffer bs) = Buffer (BS.unsafeDrop (fromIntegral n) bs)
bufferUnsafeTake :: Word -> Buffer -> Buffer
bufferUnsafeTake n (Buffer bs) = Buffer (BS.unsafeTake (fromIntegral n) bs)
bufferUnsafeTail :: Buffer -> Buffer
bufferUnsafeTail (Buffer bs) = Buffer (BS.unsafeTail bs)
bufferUnsafeHead :: Buffer -> Word8
bufferUnsafeHead (Buffer bs) = BS.unsafeHead bs
bufferUnsafeLast :: Buffer -> Word8
bufferUnsafeLast (Buffer bs) = BS.unsafeLast bs
bufferUnsafeInit :: Buffer -> Buffer
bufferUnsafeInit (Buffer bs) = Buffer (BS.unsafeInit bs)
bufferUnsafeIndex :: Buffer -> Word -> Word8
bufferUnsafeIndex (Buffer bs) n = BS.unsafeIndex bs (fromIntegral n)
bufferUnsafeMapMemory :: MonadIO m => Word -> Ptr () -> m Buffer
bufferUnsafeMapMemory sz ptr =
Buffer <$> liftIO (BS.unsafePackCStringLen (castPtr ptr, fromIntegral sz))
bufferUnsafeUsePtr :: MonadInIO m => Buffer -> (Ptr () -> Word -> m a) -> m a
bufferUnsafeUsePtr bu@(Buffer b) f =
liftWith (BS.unsafeUseAsCString b) $ \p ->
f (castPtr p) (bufferSize bu)
bufferReadFile :: MonadIO m => FilePath -> m Buffer
bufferReadFile path = Buffer <$> liftIO (BS.readFile path)
bufferWriteFile :: MonadIO m => FilePath -> Buffer -> m ()
bufferWriteFile path (Buffer bs) = liftIO (BS.writeFile path bs)