data-array-byte (empty) → 0.1
raw patch · 5 files changed
+441/−0 lines, 5 filesdep +basedep +deepseqdep +template-haskell
Dependencies added: base, deepseq, template-haskell
Files
- Data/Array/Byte.hs +353/−0
- LICENSE +30/−0
- README.md +11/−0
- changelog.md +3/−0
- data-array-byte.cabal +44/−0
+ Data/Array/Byte.hs view
@@ -0,0 +1,353 @@+-- |+-- Module : Data.Array.Byte+-- Copyright : (c) Roman Leshchinskiy 2009-2012+-- License : BSD-style+--+-- Compatibility layer for+-- <https://hackage.haskell.org/package/base/docs/Data-Array-Byte.html Data.Array.Byte>,+-- providing boxed wrappers for `ByteArray#` and `MutableByteArray#`+-- and relevant instances for GHC < 9.4. Include it into your Cabal file:+--+-- > build-depends: base+-- > if impl(ghc < 9.4)+-- > build-depends: data-array-byte+--+-- and then import "Data.Array.Byte" unconditionally.+--+-- Originally derived from @primitive@ package.++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UnliftedFFITypes #-}++module Data.Array.Byte (+ ByteArray(..),+ MutableByteArray(..),+) where++import Data.Bits ((.&.), unsafeShiftR)+import Data.Data (mkNoRepType, Data(..), Typeable)+import qualified Data.Foldable as F+import Data.Semigroup (Semigroup(..))+import GHC.Exts (ByteArray#, MutableByteArray#, sameMutableByteArray#, isTrue#, unsafeCoerce#, reallyUnsafePtrEquality#, copyByteArray#, writeWord8Array#, indexWord8Array#, sizeofByteArray#, unsafeFreezeByteArray#, newByteArray#, IsList(..), Int(..))+import GHC.Show (intToDigit)+import GHC.ST (ST(..), runST)+import GHC.Word (Word8(..))++#if MIN_VERSION_base(4,11,0)+import GHC.Exts (compareByteArrays#)+#else+import Foreign.C.Types (CInt(..), CSize(..))+import System.IO.Unsafe (unsafeDupablePerformIO)+#endif++import Control.DeepSeq (NFData(..))++import GHC.Exts (Addr#, copyAddrToByteArray#)+import Language.Haskell.TH.Syntax (Lift(..), Lit(..), Exp(..))++#if MIN_VERSION_template_haskell(2,17,0)+import Language.Haskell.TH.Syntax (unsafeCodeCoerce)+#elif MIN_VERSION_template_haskell(2,16,0)+import Language.Haskell.TH.Syntax (unsafeTExpCoerce)+#endif++#if MIN_VERSION_template_haskell(2,16,0)+import GHC.ForeignPtr (ForeignPtr(..), ForeignPtrContents(..))+import GHC.Exts (newPinnedByteArray#, isByteArrayPinned#, byteArrayContents#)+import Language.Haskell.TH.Syntax (Bytes(..))+#endif++-- | Boxed wrapper for 'ByteArray#'.+--+-- Since 'ByteArray#' is an unlifted type and not a member of kind 'Data.Kind.Type',+-- things like @[ByteArray#]@ or @IO ByteArray#@ are ill-typed. To work around this+-- inconvenience this module provides a standard boxed wrapper, inhabiting 'Data.Kind.Type'.+-- Clients are expected to use 'ByteArray' in higher-level APIs,+-- but wrap and unwrap 'ByteArray' internally as they please+-- and use functions from "GHC.Exts".+data ByteArray = ByteArray ByteArray#++-- | Boxed wrapper for 'MutableByteArray#'.+--+-- Since 'MutableByteArray#' is an unlifted type and not a member of kind 'Data.Kind.Type',+-- things like @[MutableByteArray#]@ or @IO MutableByteArray#@ are ill-typed. To work around this+-- inconvenience this module provides a standard boxed wrapper, inhabiting 'Data.Kind.Type'.+-- Clients are expected to use 'MutableByteArray' in higher-level APIs,+-- but wrap and unwrap 'MutableByteArray' internally as they please+-- and use functions from "GHC.Exts".+data MutableByteArray s = MutableByteArray (MutableByteArray# s)++-- | Create a new mutable byte array of the specified size in bytes.+--+-- /Note:/ this function does not check if the input is non-negative.+newByteArray :: Int -> ST s (MutableByteArray s)+{-# INLINE newByteArray #-}+newByteArray (I# n#) =+ ST (\s# -> case newByteArray# n# s# of+ (# s'#, arr# #) -> (# s'#, MutableByteArray arr# #))++-- | Convert a mutable byte array to an immutable one without copying. The+-- array should not be modified after the conversion.+unsafeFreezeByteArray :: MutableByteArray s -> ST s ByteArray+{-# INLINE unsafeFreezeByteArray #-}+unsafeFreezeByteArray (MutableByteArray arr#) =+ ST (\s# -> case unsafeFreezeByteArray# arr# s# of+ (# s'#, arr'# #) -> (# s'#, ByteArray arr'# #))++-- | Size of the byte array in bytes.+sizeofByteArray :: ByteArray -> Int+{-# INLINE sizeofByteArray #-}+sizeofByteArray (ByteArray arr#) = I# (sizeofByteArray# arr#)++-- | Read byte at specific index.+indexByteArray :: ByteArray -> Int -> Word8+{-# INLINE indexByteArray #-}+indexByteArray (ByteArray arr#) (I# i#) = W8# (indexWord8Array# arr# i#)++-- | Write byte at specific index.+writeByteArray :: MutableByteArray s -> Int -> Word8 -> ST s ()+{-# INLINE writeByteArray #-}+writeByteArray (MutableByteArray arr#) (I# i#) (W8# x#) =+ ST (\s# -> case writeWord8Array# arr# i# x# s# of+ s'# -> (# s'#, () #))++-- | Explode 'ByteArray' into a list of bytes.+byteArrayToList :: ByteArray -> [Word8]+{-# INLINE byteArrayToList #-}+byteArrayToList arr = go 0+ where+ go i+ | i < maxI = indexByteArray arr i : go (i+1)+ | otherwise = []+ maxI = sizeofByteArray arr++-- | Create a 'ByteArray' from a list of a known length. If the length+-- of the list does not match the given length, this throws an exception.+byteArrayFromListN :: Int -> [Word8] -> ByteArray+byteArrayFromListN n ys = runST $ do+ marr <- newByteArray n+ let go !ix [] = if ix == n+ then return ()+ else error $ "Data.Array.Byte.byteArrayFromListN: list length less than specified size"+ go !ix (x : xs) = if ix < n+ then do+ writeByteArray marr ix x+ go (ix + 1) xs+ else error $ "Data.Array.Byte.byteArrayFromListN: list length greater than specified size"+ go 0 ys+ unsafeFreezeByteArray marr++-- | Copy a slice of an immutable byte array to a mutable byte array.+--+-- /Note:/ this function does not do bounds or overlap checking.+copyByteArray+ :: MutableByteArray s -- ^ destination array+ -> Int -- ^ offset into destination array+ -> ByteArray -- ^ source array+ -> Int -- ^ offset into source array+ -> Int -- ^ number of bytes to copy+ -> ST s ()+{-# INLINE copyByteArray #-}+copyByteArray (MutableByteArray dst#) (I# doff#) (ByteArray src#) (I# soff#) (I# sz#) =+ ST (\s# -> case copyByteArray# src# soff# dst# doff# sz# s# of+ s'# -> (# s'#, () #))++instance Data ByteArray where+ toConstr _ = error "toConstr"+ gunfold _ _ = error "gunfold"+ dataTypeOf _ = mkNoRepType "Data.Array.Byte.ByteArray"++instance Typeable s => Data (MutableByteArray s) where+ toConstr _ = error "toConstr"+ gunfold _ _ = error "gunfold"+ dataTypeOf _ = mkNoRepType "Data.Array.Byte.MutableByteArray"++instance Show ByteArray where+ showsPrec _ ba =+ showString "[" . go 0+ where+ showW8 :: Word8 -> String -> String+ showW8 !w s =+ '0'+ : 'x'+ : intToDigit (fromIntegral (unsafeShiftR w 4))+ : intToDigit (fromIntegral (w .&. 0x0F))+ : s+ go i+ | i < sizeofByteArray ba = comma . showW8 (indexByteArray ba i :: Word8) . go (i+1)+ | otherwise = showChar ']'+ where+ comma | i == 0 = id+ | otherwise = showString ", "++-- | Compare prefixes of given length.+compareByteArraysFromBeginning :: ByteArray -> ByteArray -> Int -> Ordering+{-# INLINE compareByteArraysFromBeginning #-}+#if MIN_VERSION_base(4,11,0)+compareByteArraysFromBeginning (ByteArray ba1#) (ByteArray ba2#) (I# n#)+ = compare (I# (compareByteArrays# ba1# 0# ba2# 0# n#)) 0+#else+compareByteArraysFromBeginning (ByteArray ba1#) (ByteArray ba2#) (I# n#)+ = compare (fromCInt (unsafeDupablePerformIO (memcmp ba1# ba2# n))) 0+ where+ n = fromIntegral (I# n#) :: CSize+ fromCInt = fromIntegral :: CInt -> Int++foreign import ccall unsafe "memcmp"+ memcmp :: ByteArray# -> ByteArray# -> CSize -> IO CInt+#endif+++-- | Do two byte arrays share the same pointer?+sameByteArray :: ByteArray# -> ByteArray# -> Bool+sameByteArray ba1 ba2 =+ case reallyUnsafePtrEquality# (unsafeCoerce# ba1 :: ()) (unsafeCoerce# ba2 :: ()) of+ r -> isTrue# r++instance Eq ByteArray where+ ba1@(ByteArray ba1#) == ba2@(ByteArray ba2#)+ | sameByteArray ba1# ba2# = True+ | n1 /= n2 = False+ | otherwise = compareByteArraysFromBeginning ba1 ba2 n1 == EQ+ where+ n1 = sizeofByteArray ba1+ n2 = sizeofByteArray ba2++instance Eq (MutableByteArray s) where+ (==) (MutableByteArray arr#) (MutableByteArray brr#)+ = isTrue# (sameMutableByteArray# arr# brr#)++-- | Non-lexicographic ordering. This compares the lengths of+-- the byte arrays first and uses a lexicographic ordering if+-- the lengths are equal. Subject to change between major versions.+instance Ord ByteArray where+ ba1@(ByteArray ba1#) `compare` ba2@(ByteArray ba2#)+ | sameByteArray ba1# ba2# = EQ+ | n1 /= n2 = n1 `compare` n2+ | otherwise = compareByteArraysFromBeginning ba1 ba2 n1+ where+ n1 = sizeofByteArray ba1+ n2 = sizeofByteArray ba2+-- The primop compareByteArrays# (invoked from 'compareByteArraysFromBeginning')+-- performs a check for pointer equality as well. However, it+-- is included here because it is likely better to check for pointer equality+-- before checking for length equality. Getting the length requires deferencing+-- the pointers, which could cause accesses to memory that is not in the cache.+-- By contrast, a pointer equality check is always extremely cheap.++-- | Append two byte arrays.+appendByteArray :: ByteArray -> ByteArray -> ByteArray+appendByteArray a b = runST $ do+ marr <- newByteArray (sizeofByteArray a + sizeofByteArray b)+ copyByteArray marr 0 a 0 (sizeofByteArray a)+ copyByteArray marr (sizeofByteArray a) b 0 (sizeofByteArray b)+ unsafeFreezeByteArray marr++-- | Concatenate a list of 'ByteArray's.+concatByteArray :: [ByteArray] -> ByteArray+concatByteArray arrs = runST $ do+ let len = calcLength arrs 0+ marr <- newByteArray len+ pasteByteArrays marr 0 arrs+ unsafeFreezeByteArray marr++-- | Dump immutable 'ByteArray's into a mutable one, starting from a given offset.+pasteByteArrays :: MutableByteArray s -> Int -> [ByteArray] -> ST s ()+pasteByteArrays !_ !_ [] = return ()+pasteByteArrays !marr !ix (x : xs) = do+ copyByteArray marr ix x 0 (sizeofByteArray x)+ pasteByteArrays marr (ix + sizeofByteArray x) xs++-- | Compute total length of 'ByteArray's, increased by accumulator.+calcLength :: [ByteArray] -> Int -> Int+calcLength [] !n = n+calcLength (x : xs) !n = calcLength xs (sizeofByteArray x + n)++-- | An array of zero length.+emptyByteArray :: ByteArray+emptyByteArray = runST (newByteArray 0 >>= unsafeFreezeByteArray)++-- | Replicate 'ByteArray' given number of times and concatenate all together.+replicateByteArray :: Int -> ByteArray -> ByteArray+replicateByteArray n arr = runST $ do+ marr <- newByteArray (n * sizeofByteArray arr)+ let go i = if i < n+ then do+ copyByteArray marr (i * sizeofByteArray arr) arr 0 (sizeofByteArray arr)+ go (i + 1)+ else return ()+ go 0+ unsafeFreezeByteArray marr++instance Semigroup ByteArray where+ (<>) = appendByteArray+ sconcat = mconcat . F.toList+ stimes i arr+ | itgr < 1 = emptyByteArray+ | itgr <= (fromIntegral (maxBound :: Int)) = replicateByteArray (fromIntegral itgr) arr+ | otherwise = error "Data.Array.Byte#stimes: cannot allocate the requested amount of memory"+ where itgr = toInteger i :: Integer++instance Monoid ByteArray where+ mempty = emptyByteArray+ mappend = (<>)+ mconcat = concatByteArray++instance IsList ByteArray where+ type Item ByteArray = Word8++ toList = byteArrayToList+ fromList xs = byteArrayFromListN (length xs) xs+ fromListN = byteArrayFromListN++instance NFData ByteArray where+ rnf (ByteArray _) = ()++instance NFData (MutableByteArray s) where+ rnf (MutableByteArray _) = ()++instance Lift ByteArray where+#if MIN_VERSION_template_haskell(2,17,0)+ liftTyped = unsafeCodeCoerce . lift+#elif MIN_VERSION_template_haskell(2,16,0)+ liftTyped = unsafeTExpCoerce . lift+#endif++#if MIN_VERSION_template_haskell(2,16,0)+ lift (ByteArray b) = return+ (AppE (AppE (VarE 'addrToByteArray) (LitE (IntegerL (fromIntegral len))))+ (LitE (BytesPrimL (Bytes ptr 0 (fromIntegral len)))))+ where+ len# = sizeofByteArray# b+ len = I# len#+ pb :: ByteArray#+ !(ByteArray pb)+ | isTrue# (isByteArrayPinned# b) = ByteArray b+ | otherwise = runST $ ST $+ \s -> case newPinnedByteArray# len# s of+ (# s', mb #) -> case copyByteArray# b 0# mb 0# len# s' of+ s'' -> case unsafeFreezeByteArray# mb s'' of+ (# s''', ret #) -> (# s''', ByteArray ret #)+ ptr :: ForeignPtr Word8+ ptr = ForeignPtr (byteArrayContents# pb) (PlainPtr (unsafeCoerce# pb))+#else+ lift (ByteArray b) = return+ (AppE (AppE (VarE 'addrToByteArray) (LitE (IntegerL (fromIntegral len))))+ (LitE (StringPrimL (toList (ByteArray b)))))+ where+ len# = sizeofByteArray# b+ len = I# len#+#endif++addrToByteArray :: Int -> Addr# -> ByteArray+addrToByteArray (I# len) addr = runST $ ST $+ \s -> case newByteArray# len s of+ (# s', mb #) -> case copyAddrToByteArray# addr mb 0# len s' of+ s'' -> case unsafeFreezeByteArray# mb s'' of+ (# s''', ret #) -> (# s''', ByteArray ret #)
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2008-2009, Roman Leshchinskiy+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+this list of conditions and the following disclaimer.+ +- Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.+ +- Neither name of the University nor the names of its contributors may be+used to endorse or promote products derived from this software without+specific prior written permission. ++THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF+GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH+DAMAGE.+
+ README.md view
@@ -0,0 +1,11 @@+# data-array-byte++Compatibility layer for [`Data.Array.Byte`](https://hackage.haskell.org/package/base/docs/Data-Array-Byte.html), providing boxed wrappers for [`ByteArray#`](https://hackage.haskell.org/package/base/docs/GHC-Exts.html#t:ByteArray-35-) and [`MutableByteArray#`](https://hackage.haskell.org/package/base/docs/GHC-Exts.html#t:MutableByteArray-35-) and relevant instances for GHC < 9.4. Include it into your Cabal file:++```cabal+build-depends: base+if impl(ghc < 9.4)+ build-depends: data-array-byte+```++and then `import Data.Array.Byte` unconditionally.
+ changelog.md view
@@ -0,0 +1,3 @@+# 0.1++* Initial release, matching `Data.Array.Byte` in `base-4.17.0.0` and GHC 9.4.
+ data-array-byte.cabal view
@@ -0,0 +1,44 @@+cabal-version: 2.0+name: data-array-byte+version: 0.1+license: BSD3+license-file: LICENSE+copyright: (c) Roman Leshchinskiy 2009-2012+maintainer: andrew.lelechenko@gmail.com+author: Roman Leshchinskiy <rl@cse.unsw.edu.au>+tested-with:+ ghc ==8.0.2 ghc ==8.2.2 ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.4+ ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.4 ghc ==9.4.2++homepage: https://github.com/Bodigrim/data-array-byte+bug-reports: https://github.com/Bodigrim/data-array-byte/issues+synopsis: Compatibility layer for Data.Array.Byte+description:+ Compatibility layer for [Data.Array.Byte](https://hackage.haskell.org/package/base/docs/Data-Array-Byte.html), providing boxed wrappers for @ByteArray#@ and @MutableByteArray#@ and relevant instances for GHC < 9.4. Include it into your Cabal file:+ .+ > build-depends: base+ > if impl(ghc < 9.4)+ > build-depends: data-array-byte+ .+ and then @import Data.Array.Byte@ unconditionally.++category: Compatibility+build-type: Simple+extra-source-files:+ changelog.md+ README.md++source-repository head+ type: git+ location: https://github.com/Bodigrim/data-array-byte++library+ default-language: Haskell2010+ ghc-options: -Wall+ build-depends:+ base >=4.9 && <4.18,+ deepseq >=1.4 && <1.5,+ template-haskell >=2.11 && <2.20++ if impl(ghc <9.4)+ exposed-modules: Data.Array.Byte