{-# LANGUAGE FlexibleInstances #-} module HaskellWorks.Data.ByteString ( chunkedBy , ToByteString(..) , ToByteStrings(..) , resegment , resegmentPadded , rechunk ) where import Data.Semigroup ((<>)) import Data.Word import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BSI import qualified Data.ByteString.Lazy as LBS import qualified Data.Vector.Storable as DVS class ToByteString a where toByteString :: a -> BS.ByteString instance ToByteString BS.ByteString where toByteString = id {-# INLINE toByteString #-} instance ToByteString (DVS.Vector Word8) where toByteString v = case DVS.unsafeToForeignPtr v of (fptr, start, offset) -> BSI.fromForeignPtr fptr start offset {-# INLINE toByteString #-} instance ToByteString (DVS.Vector Word16) where toByteString v = case DVS.unsafeToForeignPtr (DVS.unsafeCast v :: DVS.Vector Word8) of (fptr, start, offset) -> BSI.fromForeignPtr fptr start offset {-# INLINE toByteString #-} instance ToByteString (DVS.Vector Word32) where toByteString v = case DVS.unsafeToForeignPtr (DVS.unsafeCast v :: DVS.Vector Word8) of (fptr, start, offset) -> BSI.fromForeignPtr fptr start offset {-# INLINE toByteString #-} instance ToByteString (DVS.Vector Word64) where toByteString v = case DVS.unsafeToForeignPtr (DVS.unsafeCast v :: DVS.Vector Word8) of (fptr, start, offset) -> BSI.fromForeignPtr fptr start offset {-# INLINE toByteString #-} class ToByteStrings a where toByteStrings :: a -> [BS.ByteString] instance ToByteStrings [BS.ByteString] where toByteStrings = id {-# INLINE toByteStrings #-} instance ToByteStrings LBS.ByteString where toByteStrings = LBS.toChunks {-# INLINE toByteStrings #-} instance ToByteStrings BS.ByteString where toByteStrings = (:[]) {-# INLINE toByteStrings #-} instance ToByteStrings (DVS.Vector Word8) where toByteStrings = (:[]) . toByteString {-# INLINE toByteStrings #-} instance ToByteStrings (DVS.Vector Word16) where toByteStrings = (:[]) . toByteString {-# INLINE toByteStrings #-} instance ToByteStrings (DVS.Vector Word32) where toByteStrings = (:[]) . toByteString {-# INLINE toByteStrings #-} instance ToByteStrings (DVS.Vector Word64) where toByteStrings = (:[]) . toByteString {-# INLINE toByteStrings #-} instance ToByteStrings [DVS.Vector Word8] where toByteStrings = (toByteString <$>) {-# INLINE toByteStrings #-} instance ToByteStrings [DVS.Vector Word16] where toByteStrings = (toByteString <$>) {-# INLINE toByteStrings #-} instance ToByteStrings [DVS.Vector Word32] where toByteStrings = (toByteString <$>) {-# INLINE toByteStrings #-} instance ToByteStrings [DVS.Vector Word64] where toByteStrings = (toByteString <$>) {-# INLINE toByteStrings #-} -- | Chunk a @bs into list of smaller byte strings of no more than @n elements chunkedBy :: Int -> BS.ByteString -> [BS.ByteString] chunkedBy n bs = if BS.length bs == 0 then [] else case BS.splitAt n bs of (as, zs) -> as : chunkedBy n zs {-# INLINE chunkedBy #-} rechunk :: Int -> [BS.ByteString] -> [BS.ByteString] rechunk size = go where go (bs:bss) = let bsLen = BS.length bs in if bsLen < size then case size - bsLen of bsNeed -> case bss of (cs:css) -> case BS.length cs of csLen | csLen > bsNeed -> (bs <> BS.take bsNeed cs ):go (BS.drop bsNeed cs:css) csLen | csLen == bsNeed -> (bs <> cs ):go css _ | otherwise -> go ((bs <> cs) :css) [] -> [bs] else if size == bsLen then bs:go bss else BS.take size bs:go (BS.drop size bs:bss) go [] = [] resegment :: Int -> [BS.ByteString] -> [BS.ByteString] resegment multiple = go where go (bs:bss) = case BS.length bs of bsLen -> if bsLen < multiple then case multiple - bsLen of bsNeed -> case bss of (cs:css) -> case BS.length cs of csLen | csLen > bsNeed -> (bs <> BS.take bsNeed cs ):go (BS.drop bsNeed cs:css) csLen | csLen == bsNeed -> (bs <> cs ):go css _ | otherwise -> go ((bs <> cs) :css) [] -> [bs] else case (bsLen `div` multiple) * multiple of bsCroppedLen -> if bsCroppedLen == bsLen then bs:go bss else BS.take bsCroppedLen bs:go (BS.drop bsCroppedLen bs:bss) go [] = [] resegmentPadded :: Int -> [BS.ByteString] -> [BS.ByteString] resegmentPadded multiple = go where go (bs:bss) = case BS.length bs of bsLen -> if bsLen < multiple then case multiple - bsLen of bsNeed -> case bss of (cs:css) -> case BS.length cs of csLen | csLen > bsNeed -> (bs <> BS.take bsNeed cs ):go (BS.drop bsNeed cs:css) csLen | csLen == bsNeed -> (bs <> cs ):go css _ | otherwise -> go ((bs <> cs) :css) [] -> [bs <> BS.replicate bsNeed 0] else case (bsLen `div` multiple) * multiple of bsCroppedLen -> if bsCroppedLen == bsLen then bs:go bss else BS.take bsCroppedLen bs:go (BS.drop bsCroppedLen bs:bss) go [] = []