{-# LANGUAGE BangPatterns #-}

{- | Per-column sinks for the default CSV reader. Typed columns (explicit
schema) parse straight from the input slice into the WS-C builders;
inferred and EitherRead columns record unboxed @(start, end)@ offsets
into the input buffer (plus a cold side map for quote-unescaped cells)
for the inference pass ("DataFrame.IO.CSV.Internal.Infer").
-}
module DataFrame.IO.CSV.Internal.Sink (
    Env (..),
    MissingMode (..),
    Sink (..),
    SliceCol,
    Cells (..),
    cellAt,
    withCell,
    newSliceCol,
    feedSink,
    nullSink,
    freezeCells,
    sliceBS,
) where

import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BSU
import qualified Data.IntMap.Strict as IM
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM

import Control.Monad.ST (RealWorld, stToIO)
import Data.IORef
import Data.Word (Word8)
import DataFrame.IO.CSV.Internal.Scanner
import DataFrame.Internal.ColumnBuilder
import DataFrame.Internal.Parsing.Fast (
    isMissingFieldSlice,
    parseDoubleFieldSlice,
    parseIntFieldSlice,
 )
import Foreign.Ptr (Ptr, castPtr, plusPtr)

{- | How missing tokens are detected (audit D2: canonical list goes
through the WS-B memcmp fast path; custom lists decode and compare).
-}
data MissingMode = MissNone | MissCanonical | MissCustom

-- | Read-time environment shared by every sink.
data Env = Env
    { Env -> ByteString
envBS :: !BS.ByteString
    -- ^ Whole input (BOM-stripped when the caller stripped it).
    , Env -> Ptr Word8
envPtr :: !(Ptr Word8)
    -- ^ Base pointer of 'envBS' (valid for the whole decode).
    , Env -> MissingMode
envMode :: !MissingMode
    , Env -> [Text]
envTexts :: ![T.Text]
    -- ^ The effective missing-indicator list ('Data.Text' level).
    }

data Sink
    = SinkInt !(IntBuilder RealWorld)
    | SinkDouble !(DoubleBuilder RealWorld)
    | SinkText !(TextBuilder RealWorld)
    | SinkBS !SliceCol

{- | Growable cell store for inferred columns: per cell a @(start, end)@
offset pair into the input (already Char8-stripped) and a 0\/1 validity
byte. Quote-unescaped cells (cold) live in an 'IM.IntMap' keyed by row;
their offset pair is @(-1, row)@.
-}
data SliceCol = SliceCol
    { SliceCol -> IORef (IOVector Int)
scOffs :: !(IORef (VUM.IOVector Int))
    , SliceCol -> IORef (IOVector Word8)
scValid :: !(IORef (VUM.IOVector Word8))
    , SliceCol -> IORef (IntMap ByteString)
scEsc :: !(IORef (IM.IntMap BS.ByteString))
    }

-- | Frozen 'SliceCol' plus the buffer it points into.
data Cells = Cells
    { Cells -> ByteString
cBS :: !BS.ByteString
    , Cells -> Vector Int
cOffs :: !(VU.Vector Int)
    , Cells -> IntMap ByteString
cEsc :: !(IM.IntMap BS.ByteString)
    , Cells -> Vector Word8
cValid :: !(VU.Vector Word8)
    , Cells -> Int
cLen :: !Int
    }

{- | Resolve cell @i@ as a slice and continue (no 'BS.ByteString' header
is allocated for the common in-buffer case).
-}
withCell :: Cells -> Int -> (BS.ByteString -> Int -> Int -> r) -> r
withCell :: forall r. Cells -> Int -> (ByteString -> Int -> Int -> r) -> r
withCell Cells
c !Int
i ByteString -> Int -> Int -> r
k =
    let s :: Int
s = Vector Int -> Int -> Int
forall a. Unbox a => Vector a -> Int -> a
VU.unsafeIndex (Cells -> Vector Int
cOffs Cells
c) (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i)
        e :: Int
e = Vector Int -> Int -> Int
forall a. Unbox a => Vector a -> Int -> a
VU.unsafeIndex (Cells -> Vector Int
cOffs Cells
c) (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
     in if Int
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0
            then ByteString -> Int -> Int -> r
k (Cells -> ByteString
cBS Cells
c) Int
s Int
e
            else let cell :: ByteString
cell = Cells -> IntMap ByteString
cEsc Cells
c IntMap ByteString -> Int -> ByteString
forall a. IntMap a -> Int -> a
IM.! Int
e in ByteString -> Int -> Int -> r
k ByteString
cell Int
0 (ByteString -> Int
BS.length ByteString
cell)
{-# INLINE withCell #-}

-- | Cell @i@ as a (zero-copy) 'BS.ByteString'.
cellAt :: Cells -> Int -> BS.ByteString
cellAt :: Cells -> Int -> ByteString
cellAt Cells
c Int
i = Cells
-> Int -> (ByteString -> Int -> Int -> ByteString) -> ByteString
forall r. Cells -> Int -> (ByteString -> Int -> Int -> r) -> r
withCell Cells
c Int
i ByteString -> Int -> Int -> ByteString
sliceBS
{-# INLINE cellAt #-}

newSliceCol :: Int -> IO SliceCol
newSliceCol :: Int -> IO SliceCol
newSliceCol Int
hint = do
    let cap :: Int
cap = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
16 Int
hint
    IOVector Int
offs <- Int -> IO (MVector (PrimState IO) Int)
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
Int -> m (MVector (PrimState m) a)
VUM.unsafeNew (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
cap)
    IOVector Word8
val <- Int -> IO (MVector (PrimState IO) Word8)
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
Int -> m (MVector (PrimState m) a)
VUM.unsafeNew Int
cap
    IORef (IOVector Int)
-> IORef (IOVector Word8) -> IORef (IntMap ByteString) -> SliceCol
SliceCol (IORef (IOVector Int)
 -> IORef (IOVector Word8) -> IORef (IntMap ByteString) -> SliceCol)
-> IO (IORef (IOVector Int))
-> IO
     (IORef (IOVector Word8) -> IORef (IntMap ByteString) -> SliceCol)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IOVector Int -> IO (IORef (IOVector Int))
forall a. a -> IO (IORef a)
newIORef IOVector Int
offs IO
  (IORef (IOVector Word8) -> IORef (IntMap ByteString) -> SliceCol)
-> IO (IORef (IOVector Word8))
-> IO (IORef (IntMap ByteString) -> SliceCol)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IOVector Word8 -> IO (IORef (IOVector Word8))
forall a. a -> IO (IORef a)
newIORef IOVector Word8
val IO (IORef (IntMap ByteString) -> SliceCol)
-> IO (IORef (IntMap ByteString)) -> IO SliceCol
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IntMap ByteString -> IO (IORef (IntMap ByteString))
forall a. a -> IO (IORef a)
newIORef IntMap ByteString
forall a. IntMap a
IM.empty

appendSliceCol :: SliceCol -> Int -> Int -> Int -> Word8 -> IO ()
appendSliceCol :: SliceCol -> Int -> Int -> Int -> Word8 -> IO ()
appendSliceCol (SliceCol IORef (IOVector Int)
oRef IORef (IOVector Word8)
vRef IORef (IntMap ByteString)
_) !Int
row !Int
s !Int
e !Word8
ok = do
    IOVector Int
offs <- IORef (IOVector Int) -> IO (IOVector Int)
forall a. IORef a -> IO a
readIORef IORef (IOVector Int)
oRef
    IOVector Int
offs' <-
        if Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
row Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< IOVector Int -> Int
forall a s. Unbox a => MVector s a -> Int
VUM.length IOVector Int
offs
            then IOVector Int -> IO (IOVector Int)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IOVector Int
offs
            else do
                IOVector Int
o <- MVector (PrimState IO) Int
-> Int -> IO (MVector (PrimState IO) Int)
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m (MVector (PrimState m) a)
VUM.unsafeGrow IOVector Int
MVector (PrimState IO) Int
offs (IOVector Int -> Int
forall a s. Unbox a => MVector s a -> Int
VUM.length IOVector Int
offs)
                IORef (IOVector Int) -> IOVector Int -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (IOVector Int)
oRef IOVector Int
o
                IOVector Int -> IO (IOVector Int)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IOVector Int
o
    MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
offs' (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
row) Int
s
    MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
offs' (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
row Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
e
    IOVector Word8
val <- IORef (IOVector Word8) -> IO (IOVector Word8)
forall a. IORef a -> IO a
readIORef IORef (IOVector Word8)
vRef
    IOVector Word8
val' <-
        if Int
row Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< IOVector Word8 -> Int
forall a s. Unbox a => MVector s a -> Int
VUM.length IOVector Word8
val
            then IOVector Word8 -> IO (IOVector Word8)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IOVector Word8
val
            else do
                IOVector Word8
v <- MVector (PrimState IO) Word8
-> Int -> IO (MVector (PrimState IO) Word8)
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m (MVector (PrimState m) a)
VUM.unsafeGrow IOVector Word8
MVector (PrimState IO) Word8
val (IOVector Word8 -> Int
forall a s. Unbox a => MVector s a -> Int
VUM.length IOVector Word8
val)
                IORef (IOVector Word8) -> IOVector Word8 -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (IOVector Word8)
vRef IOVector Word8
v
                IOVector Word8 -> IO (IOVector Word8)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IOVector Word8
v
    MVector (PrimState IO) Word8 -> Int -> Word8 -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Word8
MVector (PrimState IO) Word8
val' Int
row Word8
ok
{-# INLINE appendSliceCol #-}

-- | Freeze the first @n@ cells (the kept row count).
freezeCells :: BS.ByteString -> SliceCol -> Int -> IO Cells
freezeCells :: ByteString -> SliceCol -> Int -> IO Cells
freezeCells ByteString
bs (SliceCol IORef (IOVector Int)
oRef IORef (IOVector Word8)
vRef IORef (IntMap ByteString)
eRef) Int
n = do
    IOVector Int
offs <- IORef (IOVector Int) -> IO (IOVector Int)
forall a. IORef a -> IO a
readIORef IORef (IOVector Int)
oRef
    IOVector Word8
val <- IORef (IOVector Word8) -> IO (IOVector Word8)
forall a. IORef a -> IO a
readIORef IORef (IOVector Word8)
vRef
    IntMap ByteString
esc <- IORef (IntMap ByteString) -> IO (IntMap ByteString)
forall a. IORef a -> IO a
readIORef IORef (IntMap ByteString)
eRef
    ByteString
-> Vector Int -> IntMap ByteString -> Vector Word8 -> Int -> Cells
Cells ByteString
bs
        (Vector Int -> IntMap ByteString -> Vector Word8 -> Int -> Cells)
-> IO (Vector Int)
-> IO (IntMap ByteString -> Vector Word8 -> Int -> Cells)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MVector (PrimState IO) Int -> IO (Vector Int)
forall a (m :: * -> *).
(Unbox a, PrimMonad m) =>
MVector (PrimState m) a -> m (Vector a)
VU.freeze (Int -> Int -> IOVector Int -> IOVector Int
forall a s. Unbox a => Int -> Int -> MVector s a -> MVector s a
VUM.slice Int
0 (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
n) IOVector Int
offs)
        IO (IntMap ByteString -> Vector Word8 -> Int -> Cells)
-> IO (IntMap ByteString) -> IO (Vector Word8 -> Int -> Cells)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IntMap ByteString -> IO (IntMap ByteString)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IntMap ByteString
esc
        IO (Vector Word8 -> Int -> Cells)
-> IO (Vector Word8) -> IO (Int -> Cells)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> MVector (PrimState IO) Word8 -> IO (Vector Word8)
forall a (m :: * -> *).
(Unbox a, PrimMonad m) =>
MVector (PrimState m) a -> m (Vector a)
VU.freeze (Int -> Int -> IOVector Word8 -> IOVector Word8
forall a s. Unbox a => Int -> Int -> MVector s a -> MVector s a
VUM.slice Int
0 Int
n IOVector Word8
val)
        IO (Int -> Cells) -> IO Int -> IO Cells
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
n

-- | O(1) sub-'BS.ByteString' of a buffer (no copy).
sliceBS :: BS.ByteString -> Int -> Int -> BS.ByteString
sliceBS :: ByteString -> Int -> Int -> ByteString
sliceBS ByteString
bs Int
s Int
e = Int -> ByteString -> ByteString
BSU.unsafeTake (Int
e Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s) (Int -> ByteString -> ByteString
BSU.unsafeDrop Int
s ByteString
bs)
{-# INLINE sliceBS #-}

{- | Feed one field slice @[s, e)@ of the input into a sink. @unesc@
fields (quoted with embedded @\"\"@) are unescaped into a fresh strict
'BS.ByteString' first (cold path).
-}
feedSink :: Env -> Sink -> Int -> Int -> Int -> Bool -> IO ()
feedSink :: Env -> Sink -> Int -> Int -> Int -> Bool -> IO ()
feedSink Env
env Sink
sink !Int
row !Int
s !Int
e !Bool
unesc = case Sink
sink of
    SinkBS SliceCol
sc
        | Bool
unesc -> do
            let fresh :: ByteString
fresh = ByteString -> Int -> Int -> ByteString
unescapeQuotes (Env -> ByteString
envBS Env
env) Int
s Int
e
                stripped :: ByteString
stripped = ByteString
-> Int -> Int -> (Int -> Int -> ByteString) -> ByteString
forall r. ByteString -> Int -> Int -> (Int -> Int -> r) -> r
withStripC8 ByteString
fresh Int
0 (ByteString -> Int
BS.length ByteString
fresh) (ByteString -> Int -> Int -> ByteString
sliceBS ByteString
fresh)
            IORef (IntMap ByteString)
-> (IntMap ByteString -> IntMap ByteString) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef' (SliceCol -> IORef (IntMap ByteString)
scEsc SliceCol
sc) (Int -> ByteString -> IntMap ByteString -> IntMap ByteString
forall a. Int -> a -> IntMap a -> IntMap a
IM.insert Int
row ByteString
stripped)
            SliceCol -> Int -> Int -> Int -> Word8 -> IO ()
appendSliceCol SliceCol
sc Int
row (-Int
1) Int
row (Env -> ByteString -> Word8
missingOk Env
env ByteString
stripped)
        | Bool
otherwise -> ByteString -> Int -> Int -> (Int -> Int -> IO ()) -> IO ()
forall r. ByteString -> Int -> Int -> (Int -> Int -> r) -> r
withStripC8 (Env -> ByteString
envBS Env
env) Int
s Int
e ((Int -> Int -> IO ()) -> IO ()) -> (Int -> Int -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Int
s' Int
e' ->
            let ok :: Word8
ok = case Env -> MissingMode
envMode Env
env of
                    MissingMode
MissNone -> Word8
1
                    MissingMode
MissCanonical ->
                        if ByteString -> Int -> Int -> Bool
isMissingFieldSlice (Env -> ByteString
envBS Env
env) Int
s' Int
e' then Word8
0 else Word8
1
                    MissingMode
MissCustom -> Env -> ByteString -> Word8
missingOk Env
env (ByteString -> Int -> Int -> ByteString
sliceBS (Env -> ByteString
envBS Env
env) Int
s' Int
e')
             in SliceCol -> Int -> Int -> Int -> Word8 -> IO ()
appendSliceCol SliceCol
sc Int
row Int
s' Int
e' Word8
ok
    Sink
_
        | Bool
unesc ->
            let fresh :: ByteString
fresh = ByteString -> Int -> Int -> ByteString
unescapeQuotes (Env -> ByteString
envBS Env
env) Int
s Int
e
             in ByteString -> (CStringLen -> IO ()) -> IO ()
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BSU.unsafeUseAsCStringLen ByteString
fresh ((CStringLen -> IO ()) -> IO ()) -> (CStringLen -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(Ptr CChar
p, Int
l) ->
                    Env -> Sink -> ByteString -> Ptr Word8 -> Int -> Int -> IO ()
feedTyped Env
env Sink
sink ByteString
fresh (Ptr CChar -> Ptr Word8
forall a b. Ptr a -> Ptr b
castPtr Ptr CChar
p) Int
0 Int
l
        | Bool
otherwise -> Env -> Sink -> ByteString -> Ptr Word8 -> Int -> Int -> IO ()
feedTyped Env
env Sink
sink (Env -> ByteString
envBS Env
env) (Env -> Ptr Word8
envPtr Env
env) Int
s Int
e
{-# INLINE feedSink #-}

-- | Validity byte for an already-stripped cell.
missingOk :: Env -> BS.ByteString -> Word8
missingOk :: Env -> ByteString -> Word8
missingOk Env
env ByteString
cell = case Env -> MissingMode
envMode Env
env of
    MissingMode
MissNone -> Word8
1
    MissingMode
MissCanonical -> if ByteString -> Int -> Int -> Bool
isMissingFieldSlice ByteString
cell Int
0 (ByteString -> Int
BS.length ByteString
cell) then Word8
0 else Word8
1
    MissingMode
MissCustom -> if ByteString -> Text
TE.decodeUtf8Lenient ByteString
cell Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Env -> [Text]
envTexts Env
env then Word8
0 else Word8
1

feedTyped ::
    Env -> Sink -> BS.ByteString -> Ptr Word8 -> Int -> Int -> IO ()
feedTyped :: Env -> Sink -> ByteString -> Ptr Word8 -> Int -> Int -> IO ()
feedTyped Env
env Sink
sink ByteString
bs !Ptr Word8
ptr !Int
s !Int
e = case Sink
sink of
    SinkInt IntBuilder RealWorld
b -> case ByteString -> Int -> Int -> Maybe Int
parseIntFieldSlice ByteString
bs Int
s Int
e of
        Just Int
v -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (IntBuilder RealWorld -> Int -> ST RealWorld ()
forall s. IntBuilder s -> Int -> ST s ()
appendInt IntBuilder RealWorld
b Int
v)
        Maybe Int
Nothing -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (IntBuilder RealWorld -> ST RealWorld ()
forall s. NumBuilder Int s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull IntBuilder RealWorld
b)
    SinkDouble DoubleBuilder RealWorld
b -> case ByteString -> Int -> Int -> Maybe Double
parseDoubleFieldSlice ByteString
bs Int
s Int
e of
        Just Double
v -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (DoubleBuilder RealWorld -> Double -> ST RealWorld ()
forall s. DoubleBuilder s -> Double -> ST s ()
appendDouble DoubleBuilder RealWorld
b Double
v)
        Maybe Double
Nothing -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (DoubleBuilder RealWorld -> ST RealWorld ()
forall s. NumBuilder Double s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull DoubleBuilder RealWorld
b)
    SinkText TextBuilder RealWorld
b -> Env
-> TextBuilder RealWorld
-> ByteString
-> Ptr Word8
-> Int
-> Int
-> IO ()
appendTextCell Env
env TextBuilder RealWorld
b ByteString
bs Ptr Word8
ptr Int
s Int
e
    SinkBS SliceCol
_ -> [Char] -> IO ()
forall a. HasCallStack => [Char] -> a
error [Char]
"feedTyped: SinkBS handled by feedSink"
{-# INLINE feedTyped #-}

{- | Text cells reproduce @T.strip . decodeUtf8Lenient@ plus the
missing-indicator test. Fast path: ASCII-edge slices append raw bytes
(the shared-buffer freeze decodes leniently per field); anything with
non-ASCII edges or a custom missing list goes through real 'T.strip'.
-}
appendTextCell ::
    Env ->
    TextBuilder RealWorld ->
    BS.ByteString ->
    Ptr Word8 ->
    Int ->
    Int ->
    IO ()
appendTextCell :: Env
-> TextBuilder RealWorld
-> ByteString
-> Ptr Word8
-> Int
-> Int
-> IO ()
appendTextCell Env
env TextBuilder RealWorld
b ByteString
bs !Ptr Word8
ptr !Int
s !Int
e =
    ByteString -> Int -> Int -> (Int -> Int -> IO ()) -> IO ()
forall r. ByteString -> Int -> Int -> (Int -> Int -> r) -> r
withStripAscii ByteString
bs Int
s Int
e ((Int -> Int -> IO ()) -> IO ()) -> (Int -> Int -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Int
s' Int
e' ->
        if Int
s' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
e'
            Bool -> Bool -> Bool
&& (ByteString -> Int -> Word8
BSU.unsafeIndex ByteString
bs Int
s' Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x80 Bool -> Bool -> Bool
|| ByteString -> Int -> Word8
BSU.unsafeIndex ByteString
bs (Int
e' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x80)
            then IO ()
slow
            else case Env -> MissingMode
envMode Env
env of
                MissingMode
MissNone -> Int -> Int -> IO ()
fast Int
s' Int
e'
                MissingMode
MissCanonical ->
                    if ByteString -> Int -> Int -> Bool
isMissingFieldSlice ByteString
bs Int
s' Int
e'
                        then ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (TextBuilder RealWorld -> ST RealWorld ()
forall s. TextBuilder s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull TextBuilder RealWorld
b)
                        else Int -> Int -> IO ()
fast Int
s' Int
e'
                MissingMode
MissCustom -> IO ()
slow
  where
    fast :: Int -> Int -> IO ()
fast Int
s' Int
e' = ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (TextBuilder RealWorld -> Ptr Word8 -> Int -> ST RealWorld ()
forall s. TextBuilder s -> Ptr Word8 -> Int -> ST s ()
appendTextSliceFromPtr TextBuilder RealWorld
b (Ptr Word8
ptr Ptr Word8 -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
s') (Int
e' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s'))
    slow :: IO ()
slow = do
        let t :: Text
t = Text -> Text
T.strip (ByteString -> Text
TE.decodeUtf8Lenient (ByteString -> Int -> Int -> ByteString
sliceBS ByteString
bs Int
s Int
e))
            missing :: Bool
missing = case Env -> MissingMode
envMode Env
env of
                MissingMode
MissNone -> Bool
False
                MissingMode
_ -> Text
t Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Env -> [Text]
envTexts Env
env
        if Bool
missing then ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (TextBuilder RealWorld -> ST RealWorld ()
forall s. TextBuilder s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull TextBuilder RealWorld
b) else ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (TextBuilder RealWorld -> Text -> ST RealWorld ()
forall s. TextBuilder s -> Text -> ST s ()
appendText TextBuilder RealWorld
b Text
t)
{-# INLINE appendTextCell #-}

-- | Append a null cell (pad-with-null for short rows, audit D6).
nullSink :: Sink -> Int -> IO ()
nullSink :: Sink -> Int -> IO ()
nullSink Sink
sink !Int
row = case Sink
sink of
    SinkInt IntBuilder RealWorld
b -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (IntBuilder RealWorld -> ST RealWorld ()
forall s. NumBuilder Int s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull IntBuilder RealWorld
b)
    SinkDouble DoubleBuilder RealWorld
b -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (DoubleBuilder RealWorld -> ST RealWorld ()
forall s. NumBuilder Double s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull DoubleBuilder RealWorld
b)
    SinkText TextBuilder RealWorld
b -> ST RealWorld () -> IO ()
forall a. ST RealWorld a -> IO a
stToIO (TextBuilder RealWorld -> ST RealWorld ()
forall s. TextBuilder s -> ST s ()
forall (b :: * -> *) s. ColumnBuilder b => b s -> ST s ()
appendNull TextBuilder RealWorld
b)
    SinkBS SliceCol
sc -> SliceCol -> Int -> Int -> Int -> Word8 -> IO ()
appendSliceCol SliceCol
sc Int
row Int
0 Int
0 Word8
0
{-# INLINE nullSink #-}