Skip to content

Commit e20992f

Browse files
Bodigrimandrewthad
authored andcommitted
Use data-array-byte package to provide ByteArray for GHC < 9.4
1 parent f10d945 commit e20992f

File tree

2 files changed

+6
-243
lines changed

2 files changed

+6
-243
lines changed

Data/Primitive/ByteArray.hs

Lines changed: 3 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -77,91 +77,11 @@ import Data.Word ( Word8 )
7777
import qualified GHC.Exts as Exts
7878
import GHC.Exts hiding (setByteArray#)
7979

80-
#if MIN_VERSION_base(4,17,0)
81-
82-
import Data.Array.Byte (ByteArray(..), MutableByteArray(..))
83-
84-
#else
85-
86-
import Control.DeepSeq
87-
import Data.Bits ( (.&.), unsafeShiftR )
88-
import GHC.Show ( intToDigit )
89-
import Data.Typeable ( Typeable )
90-
import Data.Data ( Data(..), mkNoRepType )
91-
import qualified Language.Haskell.TH.Syntax as TH
92-
import qualified Language.Haskell.TH.Lib as TH
93-
import qualified Data.Semigroup as SG
94-
import qualified Data.Foldable as F
95-
import System.IO.Unsafe (unsafePerformIO, unsafeDupablePerformIO)
96-
97-
-- | Byte arrays.
98-
data ByteArray = ByteArray ByteArray# deriving ( Typeable )
99-
100-
-- | Mutable byte arrays associated with a primitive state token.
101-
data MutableByteArray s = MutableByteArray (MutableByteArray# s)
102-
deriving ( Typeable )
103-
104-
-- | Respects array pinnedness for GHC >= 8.2
105-
instance TH.Lift ByteArray where
106-
#if MIN_VERSION_template_haskell(2,17,0)
107-
liftTyped ba = TH.unsafeCodeCoerce (TH.lift ba)
108-
#elif MIN_VERSION_template_haskell(2,16,0)
109-
liftTyped ba = TH.unsafeTExpCoerce (TH.lift ba)
80+
#if __GLASGOW_HASKELL__ < 804
81+
import System.IO.Unsafe (unsafeDupablePerformIO)
11082
#endif
11183

112-
lift ba =
113-
TH.appE
114-
(if small
115-
then [| fromLitAddrSmall# pinned len |]
116-
else [| fromLitAddrLarge# pinned len |])
117-
(TH.litE (TH.stringPrimL (toList ba)))
118-
where
119-
-- Pin it if the original was pinned; otherwise don't. This seems more
120-
-- logical to me than the alternatives. Anyone who wants a different
121-
-- pinnedness can just copy the compile-time byte array to one that
122-
-- matches what they want at run-time.
123-
#if __GLASGOW_HASKELL__ >= 802
124-
pinned = isByteArrayPinned ba
125-
#else
126-
pinned = True
127-
#endif
128-
len = sizeofByteArray ba
129-
small = len <= 2048
130-
131-
-- I don't think inlining these can be very helpful, so let's not
132-
-- do it.
133-
{-# NOINLINE fromLitAddrSmall# #-}
134-
fromLitAddrSmall# :: Bool -> Int -> Addr# -> ByteArray
135-
fromLitAddrSmall# pinned len ptr = inline (fromLitAddr# True pinned len ptr)
136-
137-
{-# NOINLINE fromLitAddrLarge# #-}
138-
fromLitAddrLarge# :: Bool -> Int -> Addr# -> ByteArray
139-
fromLitAddrLarge# pinned len ptr = inline (fromLitAddr# False pinned len ptr)
140-
141-
fromLitAddr# :: Bool -> Bool -> Int -> Addr# -> ByteArray
142-
fromLitAddr# small pinned !len !ptr = upIO $ do
143-
mba <- if pinned
144-
then newPinnedByteArray len
145-
else newByteArray len
146-
copyPtrToMutableByteArray mba 0 (Ptr ptr :: Ptr Word8) len
147-
unsafeFreezeByteArray mba
148-
where
149-
-- We don't care too much about duplication if the byte arrays are
150-
-- small. If they're large, we do. Since we don't allocate while
151-
-- we copy (we do it with a primop!), I don't believe the thunk
152-
-- deduplication mechanism can help us if two threads just happen
153-
-- to try to build the ByteArray at the same time.
154-
upIO
155-
| small = unsafeDupablePerformIO
156-
| otherwise = unsafePerformIO
157-
158-
instance NFData ByteArray where
159-
rnf (ByteArray _) = ()
160-
161-
instance NFData (MutableByteArray s) where
162-
rnf (MutableByteArray _) = ()
163-
164-
#endif
84+
import Data.Array.Byte (ByteArray(..), MutableByteArray(..))
16585

16686
-- | Create a new mutable byte array of the specified size in bytes.
16787
--
@@ -599,65 +519,6 @@ foreign import ccall unsafe "primitive-memops.h hsprimitive_memmove"
599519
-> MutableByteArray# s -> CPtrdiff
600520
-> CSize -> IO ()
601521

602-
#if !MIN_VERSION_base(4,17,0)
603-
604-
instance Eq (MutableByteArray s) where
605-
(==) = sameMutableByteArray
606-
607-
instance Data ByteArray where
608-
toConstr _ = error "toConstr"
609-
gunfold _ _ = error "gunfold"
610-
dataTypeOf _ = mkNoRepType "Data.Primitive.ByteArray.ByteArray"
611-
612-
instance Typeable s => Data (MutableByteArray s) where
613-
toConstr _ = error "toConstr"
614-
gunfold _ _ = error "gunfold"
615-
dataTypeOf _ = mkNoRepType "Data.Primitive.ByteArray.MutableByteArray"
616-
617-
-- | @since 0.6.3.0
618-
--
619-
-- Behavior changed in 0.7.2.0. Before 0.7.2.0, this instance rendered
620-
-- 8-bit words less than 16 as a single hexadecimal digit (e.g. 13 was @0xD@).
621-
-- Starting with 0.7.2.0, all 8-bit words are represented as two digits
622-
-- (e.g. 13 is @0x0D@).
623-
instance Show ByteArray where
624-
showsPrec _ ba =
625-
showString "[" . go 0
626-
where
627-
showW8 :: Word8 -> String -> String
628-
showW8 !w s =
629-
'0'
630-
: 'x'
631-
: intToDigit (fromIntegral (unsafeShiftR w 4))
632-
: intToDigit (fromIntegral (w .&. 0x0F))
633-
: s
634-
go i
635-
| i < sizeofByteArray ba = comma . showW8 (indexByteArray ba i :: Word8) . go (i+1)
636-
| otherwise = showChar ']'
637-
where
638-
comma | i == 0 = id
639-
| otherwise = showString ", "
640-
641-
-- Only used internally
642-
compareByteArraysFromBeginning :: ByteArray -> ByteArray -> Int -> Ordering
643-
{-# INLINE compareByteArraysFromBeginning #-}
644-
#if __GLASGOW_HASKELL__ >= 804
645-
compareByteArraysFromBeginning (ByteArray ba1#) (ByteArray ba2#) (I# n#)
646-
= compare (I# (compareByteArrays# ba1# 0# ba2# 0# n#)) 0
647-
#else
648-
-- Emulate GHC 8.4's 'GHC.Prim.compareByteArrays#'
649-
compareByteArraysFromBeginning (ByteArray ba1#) (ByteArray ba2#) (I# n#)
650-
= compare (fromCInt (unsafeDupablePerformIO (memcmp_ba ba1# ba2# n))) 0
651-
where
652-
n = fromIntegral (I# n#) :: CSize
653-
fromCInt = fromIntegral :: CInt -> Int
654-
655-
foreign import ccall unsafe "primitive-memops.h hsprimitive_memcmp"
656-
memcmp_ba :: ByteArray# -> ByteArray# -> CSize -> IO CInt
657-
#endif
658-
659-
#endif
660-
661522
-- | Lexicographic comparison of equal-length slices into two byte arrays.
662523
-- This wraps the @compareByteArrays#@ primop, which wraps @memcmp@.
663524
compareByteArrays
@@ -683,112 +544,11 @@ foreign import ccall unsafe "primitive-memops.h hsprimitive_memcmp_offset"
683544
memcmp_ba_offs :: ByteArray# -> Int# -> ByteArray# -> Int# -> CSize -> IO CInt
684545
#endif
685546

686-
#if !MIN_VERSION_base(4,17,0)
687-
688-
sameByteArray :: ByteArray# -> ByteArray# -> Bool
689-
sameByteArray ba1 ba2 =
690-
case reallyUnsafePtrEquality# (unsafeCoerce# ba1 :: ()) (unsafeCoerce# ba2 :: ()) of
691-
r -> isTrue# r
692-
693-
-- | @since 0.6.3.0
694-
instance Eq ByteArray where
695-
ba1@(ByteArray ba1#) == ba2@(ByteArray ba2#)
696-
| sameByteArray ba1# ba2# = True
697-
| n1 /= n2 = False
698-
| otherwise = compareByteArraysFromBeginning ba1 ba2 n1 == EQ
699-
where
700-
n1 = sizeofByteArray ba1
701-
n2 = sizeofByteArray ba2
702-
703-
-- | Non-lexicographic ordering. This compares the lengths of
704-
-- the byte arrays first and uses a lexicographic ordering if
705-
-- the lengths are equal. Subject to change between major versions.
706-
--
707-
-- @since 0.6.3.0
708-
instance Ord ByteArray where
709-
ba1@(ByteArray ba1#) `compare` ba2@(ByteArray ba2#)
710-
| sameByteArray ba1# ba2# = EQ
711-
| n1 /= n2 = n1 `compare` n2
712-
| otherwise = compareByteArraysFromBeginning ba1 ba2 n1
713-
where
714-
n1 = sizeofByteArray ba1
715-
n2 = sizeofByteArray ba2
716-
-- Note: On GHC 8.4, the primop compareByteArrays# performs a check for pointer
717-
-- equality as a shortcut, so the check here is actually redundant. However, it
718-
-- is included here because it is likely better to check for pointer equality
719-
-- before checking for length equality. Getting the length requires deferencing
720-
-- the pointers, which could cause accesses to memory that is not in the cache.
721-
-- By contrast, a pointer equality check is always extremely cheap.
722-
723-
appendByteArray :: ByteArray -> ByteArray -> ByteArray
724-
appendByteArray a b = runST $ do
725-
marr <- newByteArray (sizeofByteArray a + sizeofByteArray b)
726-
copyByteArray marr 0 a 0 (sizeofByteArray a)
727-
copyByteArray marr (sizeofByteArray a) b 0 (sizeofByteArray b)
728-
unsafeFreezeByteArray marr
729-
730-
concatByteArray :: [ByteArray] -> ByteArray
731-
concatByteArray arrs = runST $ do
732-
let len = calcLength arrs 0
733-
marr <- newByteArray len
734-
pasteByteArrays marr 0 arrs
735-
unsafeFreezeByteArray marr
736-
737-
pasteByteArrays :: MutableByteArray s -> Int -> [ByteArray] -> ST s ()
738-
pasteByteArrays !_ !_ [] = return ()
739-
pasteByteArrays !marr !ix (x : xs) = do
740-
copyByteArray marr ix x 0 (sizeofByteArray x)
741-
pasteByteArrays marr (ix + sizeofByteArray x) xs
742-
743-
calcLength :: [ByteArray] -> Int -> Int
744-
calcLength [] !n = n
745-
calcLength (x : xs) !n = calcLength xs (sizeofByteArray x + n)
746-
747-
#endif
748-
749547
-- | The empty 'ByteArray'.
750548
emptyByteArray :: ByteArray
751549
{-# NOINLINE emptyByteArray #-}
752550
emptyByteArray = runST (newByteArray 0 >>= unsafeFreezeByteArray)
753551

754-
#if !MIN_VERSION_base(4,17,0)
755-
756-
replicateByteArray :: Int -> ByteArray -> ByteArray
757-
replicateByteArray n arr = runST $ do
758-
marr <- newByteArray (n * sizeofByteArray arr)
759-
let go i = if i < n
760-
then do
761-
copyByteArray marr (i * sizeofByteArray arr) arr 0 (sizeofByteArray arr)
762-
go (i + 1)
763-
else return ()
764-
go 0
765-
unsafeFreezeByteArray marr
766-
767-
instance SG.Semigroup ByteArray where
768-
(<>) = appendByteArray
769-
sconcat = mconcat . F.toList
770-
stimes n arr = case compare n 0 of
771-
LT -> die "stimes" "negative multiplier"
772-
EQ -> emptyByteArray
773-
GT -> replicateByteArray (fromIntegral n) arr
774-
775-
instance Monoid ByteArray where
776-
mempty = emptyByteArray
777-
#if !(MIN_VERSION_base(4,11,0))
778-
mappend = appendByteArray
779-
#endif
780-
mconcat = concatByteArray
781-
782-
-- | @since 0.6.3.0
783-
instance Exts.IsList ByteArray where
784-
type Item ByteArray = Word8
785-
786-
toList = foldrByteArray (:) []
787-
fromList xs = byteArrayFromListN (length xs) xs
788-
fromListN = byteArrayFromListN
789-
790-
#endif
791-
792552
die :: String -> String -> a
793553
die fun problem = error $ "Data.Primitive.ByteArray." ++ fun ++ ": " ++ problem
794554

primitive.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ Library
5656
, transformers >= 0.5 && < 0.7
5757
, template-haskell >= 2.11
5858

59+
if impl(ghc < 9.4)
60+
build-depends: data-array-byte >= 0.1 && < 0.1.1
61+
5962
Ghc-Options: -O2
6063

6164
Include-Dirs: cbits

0 commit comments

Comments
 (0)