hw-json-simd-0.1.0.0: src/HaskellWorks/Data/Json/Simd/Index/Simple.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
module HaskellWorks.Data.Json.Simd.Index.Simple
( makeSimpleJsonIbBps
, makeSimpleJsonIbBpsUnsafe
, enabledMakeSimpleJsonIbBps
) where
import Control.Monad.ST
import Data.Word
import HaskellWorks.Data.Json.Simd.Internal.Index.Simple
import qualified Control.Monad.ST.Unsafe as ST
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.Mutable as DVSM
import qualified Foreign.ForeignPtr as F
import qualified Foreign.ForeignPtr.Unsafe as F
import qualified Foreign.Marshal.Unsafe as F
import qualified Foreign.Ptr as F
import qualified HaskellWorks.Data.Json.Simd.Capabilities as C
import qualified HaskellWorks.Data.Json.Simd.Internal.Foreign as F
import qualified HaskellWorks.Data.Json.Simd.Internal.List as L
import qualified System.IO.Unsafe as IO
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
{-# ANN module ("HLint: ignore Redundant do" :: String) #-}
makeSimpleJsonIbBps :: LBS.ByteString -> Either String [(BS.ByteString, BS.ByteString)]
makeSimpleJsonIbBps lbs = if enabledMakeSimpleJsonIbBps
then Right (makeSimpleJsonIbBpsUnsafe lbs)
else Left "makeSimpleJsonIbBps function is disabled"
makeSimpleJsonIbBpsUnsafe :: LBS.ByteString -> [(BS.ByteString, BS.ByteString)]
makeSimpleJsonIbBpsUnsafe lbs = L.zipPadded BS.empty BS.empty ibs bps
where chunks = makeIbs lbs
ibs = fmap (\(a, _, _) -> a) chunks
bps = ibsToIndexByteStrings chunks
makeIbs :: LBS.ByteString -> [(BS.ByteString, BS.ByteString, BS.ByteString)]
makeIbs lbs = F.unsafeLocalState $ do
wb <- allocWorkBuffers (32 * 1024 * 1204)
ws <- allocWorkState
IO.unsafeInterleaveIO $ go wb ws (LBS.toChunks lbs)
where go :: WorkBuffers -> WorkState -> [BS.ByteString] -> IO [(BS.ByteString, BS.ByteString, BS.ByteString)]
go _ _ [] = return []
go wb ws (bs:bss) = do
let resLen = BS.length bs `div` 8
resIbFptr <- F.mallocForeignPtrBytes resLen
resAFptr <- F.mallocForeignPtrBytes resLen
resBFptr <- F.mallocForeignPtrBytes resLen
let resIbPtr = F.castPtr (F.unsafeForeignPtrToPtr resIbFptr)
let resAPtr = F.castPtr (F.unsafeForeignPtrToPtr resAFptr )
let resBPtr = F.castPtr (F.unsafeForeignPtrToPtr resBFptr )
let (bsFptr, bsOff, bsLen) = BSI.toForeignPtr bs
let bsPtr = F.castPtr (F.unsafeForeignPtrToPtr bsFptr)
_ <- F.processChunk
(F.plusPtr bsPtr bsOff) -- in_buffer: Ptr UInt8
(fromIntegral bsLen) -- in_length: Size
(workBuffersD wb) -- work_bits_of_d: Ptr UInt8
(workBuffersA wb) -- work_bits_of_a: Ptr UInt8
(workBuffersZ wb) -- work_bits_of_z: Ptr UInt8
(workBuffersQ wb) -- work_bits_of_q: Ptr UInt8
(workBuffersB wb) -- work_bits_of_b: Ptr UInt8
(workBuffersE wb) -- work_bits_of_e: Ptr UInt8
(workStateZ ws) -- last_trailing_ones: Ptr Size
(workStateO ws) -- quote_odds_carry: Ptr Size
(workStateE ws) -- quote_evens_carry: Ptr Size
(workStateM ws) -- quote_mask_carry: Ptr UInt64
resIbPtr -- result_ibs: Ptr UInt8
resAPtr -- result_a: Ptr UInt8
resBPtr -- result_z: Ptr UInt8
let r =
( BSI.fromForeignPtr resIbFptr 0 resLen
, BSI.fromForeignPtr resAFptr 0 resLen
, BSI.fromForeignPtr resBFptr 0 resLen
)
rs <- IO.unsafeInterleaveIO $ go wb ws bss
return (r:rs)
ibsToIndexByteStrings :: ()
=> [(BS.ByteString, BS.ByteString, BS.ByteString)]
-> [BS.ByteString]
ibsToIndexByteStrings bits = F.unsafeLocalState $ do
bpState <- emptyBpState
IO.unsafeInterleaveIO $ go bpState (fmap (\(a, b, c) -> mkIndexStep a b c) bits)
where go :: ()
=> BpState
-> [Step]
-> IO [BS.ByteString]
go s (step:steps) = do
let bp = stepToByteString s step
bps <- IO.unsafeInterleaveIO $ go s steps
return $ bp:bps
go s [] = return [stepToByteString s indexStepFinal]
mkIndexStep :: BS.ByteString -> BS.ByteString -> BS.ByteString -> Step
mkIndexStep is as zs | isLen == asLen && asLen == zsLen = Step go isLen
where isLen = BS.length is
asLen = BS.length as
zsLen = BS.length zs
(isFptr, _, _) = BSI.toForeignPtr is
(asFptr, _, _) = BSI.toForeignPtr as
(zsFptr, _, _) = BSI.toForeignPtr zs
go :: BpState
-> DVSM.MVector s Word64
-> ST s Int
go bpState bpvm = fmap fromIntegral . ST.unsafeIOToST $ do
let (outFptr, _, _) = DVSM.unsafeToForeignPtr bpvm
F.withForeignPtr outFptr $ \outPtr ->
F.withForeignPtr isFptr $ \isPtr ->
F.withForeignPtr asFptr $ \asPtr ->
F.withForeignPtr zsFptr $ \zsPtr ->
F.withForeignPtr (bpStateP bpState) $ \bpStatePtr -> do
F.writeBpChunk
(F.castPtr isPtr)
(F.castPtr asPtr)
(F.castPtr zsPtr)
(fromIntegral (BS.length is))
(F.castPtr bpStatePtr)
(F.castPtr outPtr)
mkIndexStep _ _ _ = error "Mismatched input size"
indexStepFinal :: Step
indexStepFinal = Step go 2
where go :: BpState
-> DVSM.MVector s Word64
-> ST s Int
go bpState bpvm = fmap fromIntegral . ST.unsafeIOToST $ do
let (outFptr, _, _) = DVSM.unsafeToForeignPtr bpvm
F.withForeignPtr outFptr $ \outPtr ->
F.withForeignPtr (bpStateP bpState) $ \bpStatePtr -> do
F.writeBpChunkFinal (F.castPtr bpStatePtr) (F.castPtr outPtr)
stepToByteString :: BpState -> Step -> BS.ByteString
stepToByteString state (Step step size) = F.unsafeLocalState $ do
let bsSize = size * 8
bpFptr <- BSI.mallocByteString bsSize
let bpVm = DVSM.unsafeFromForeignPtr (F.castForeignPtr bpFptr) 0 size
w64Size <- stToIO $ step state bpVm
return (BSI.PS bpFptr 0 (w64Size * 8))
enabledMakeSimpleJsonIbBps :: Bool
enabledMakeSimpleJsonIbBps = C.avx_2 && C.sse_4_2 && C.bmi_2