|
| 1 | +{-# LANGUAGE BangPatterns #-} |
| 2 | +{-# LANGUAGE CPP #-} |
| 3 | +{-# LANGUAGE MagicHash #-} |
| 4 | +{-# LANGUAGE UnboxedTuples #-} |
| 5 | +module Data.Aeson.Internal.StrictBuilder ( |
| 6 | + toStrictByteString, |
| 7 | + toStrictByteStringIO, |
| 8 | +) where |
| 9 | + |
| 10 | +import Data.ByteString.Builder.Internal (BufferRange (BufferRange), BuildStep, Builder, fillWithBuildStep, runBuilder) |
| 11 | +import Data.ByteString.Internal (ByteString (..)) |
| 12 | +import Data.Word (Word8) |
| 13 | +import GHC.Exts (Addr#, Ptr (..), minusAddr#, plusAddr#) |
| 14 | +import GHC.Exts (Int (I#), Int#, orI#, (+#)) |
| 15 | +import GHC.Exts (MutableByteArray#, RealWorld, newPinnedByteArray#, resizeMutableByteArray#, shrinkMutableByteArray#) |
| 16 | +import GHC.ForeignPtr (ForeignPtr (ForeignPtr), ForeignPtrContents (PlainPtr)) |
| 17 | +import GHC.IO (IO (IO), unIO, unsafePerformIO) |
| 18 | + |
| 19 | +#if MIN_VERSION_base(4,16,0) |
| 20 | +import GHC.Exts (mutableByteArrayContents#) |
| 21 | +#else |
| 22 | +import GHC.Exts (byteArrayContents#, unsafeCoerce#) |
| 23 | + |
| 24 | +mutableByteArrayContents# :: MutableByteArray# s -> Addr# |
| 25 | +mutableByteArrayContents# mba = byteArrayContents# (unsafeCoerce# mba) |
| 26 | +#endif |
| 27 | + |
| 28 | +toStrictByteString :: Builder -> ByteString |
| 29 | +toStrictByteString b = unsafePerformIO (toStrictByteStringIO b) |
| 30 | +{-# NOINLINE toStrictByteString #-} |
| 31 | + |
| 32 | +toStrictByteStringIO :: Builder -> IO ByteString |
| 33 | +toStrictByteStringIO b = IO $ \s -> |
| 34 | + case newPinnedByteArray# 4096# s of |
| 35 | + (# s', mba #) -> case mutableByteArrayContents# mba of |
| 36 | + start -> unIO (toStrictByteStringWorker mba 4096# start start (plusAddr# start 4096#) (runBuilder b)) s' |
| 37 | + |
| 38 | +-- Progressively double the buffer size if it's reported to be full. |
| 39 | +-- (convertion to lazy bytestring allocates new buffer chunks). |
| 40 | +toStrictByteStringWorker |
| 41 | + :: MutableByteArray# RealWorld -- ^ the buffer bytearray |
| 42 | + -> Int# -- ^ size of the bytearray |
| 43 | + -> Addr# -- ^ beginning of the bytearray |
| 44 | + -> Addr# -- ^ current write position |
| 45 | + -> Addr# -- ^ end of the bytearray |
| 46 | + -> BuildStep () |
| 47 | + -> IO ByteString |
| 48 | +toStrictByteStringWorker mba size start begin end !curr = |
| 49 | + fillWithBuildStep curr kDone kFull kChunk (BufferRange (Ptr begin) (Ptr end)) |
| 50 | + where |
| 51 | + kDone :: Ptr Word8 -> () -> IO ByteString |
| 52 | + kDone (Ptr pos) _ = IO $ \s1 -> |
| 53 | + case minusAddr# pos start of { len -> |
| 54 | + case shrinkMutableByteArray# mba len s1 of { s2 -> |
| 55 | +#if MIN_VERSION_bytestring(0,11,0) |
| 56 | + (# s2 , BS (ForeignPtr start (PlainPtr mba)) (I# len) #) |
| 57 | +#else |
| 58 | + (# s2 , PS (ForeignPtr start (PlainPtr mba)) 0 (I# len) #) |
| 59 | +#endif |
| 60 | + }} |
| 61 | + |
| 62 | + kFull :: Ptr Word8 -> Int -> BuildStep () -> IO ByteString |
| 63 | + kFull (Ptr pos) (I# nsize) next = IO $ \s1 -> |
| 64 | + -- orI# is an approximation of max |
| 65 | + case size +# orI# size nsize of { size' -> |
| 66 | + case resizeMutableByteArray# mba size' s1 of { (# s2, mba' #) -> |
| 67 | + case mutableByteArrayContents# mba' of { start' -> |
| 68 | + unIO (toStrictByteStringWorker mba' size' start' (plusAddr# start' (minusAddr# pos start)) (plusAddr# start' size') next) s2 |
| 69 | + }}} |
| 70 | + |
| 71 | + kChunk :: Ptr Word8 -> ByteString -> BuildStep () -> IO ByteString |
| 72 | +#if MIN_VERSION_bytestring(0,11,0) |
| 73 | + kChunk (Ptr pos) (BS _ 0) next = toStrictByteStringWorker mba size start pos end next |
| 74 | +#else |
| 75 | + kChunk (Ptr pos) (PS _ _ 0) next = toStrictByteStringWorker mba size start pos end next |
| 76 | +#endif |
| 77 | + kChunk _ _ _ = fail "TODO: non-empty chunk" |
0 commit comments