dataframe-1.0.0.0: ffi/DataFrame/IO/Arrow.hs
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{- | Convert a 'DataFrame' to Arrow C Data Interface structs for zero-copy
transfer to Python (or any other Arrow consumer).
-}
module DataFrame.IO.Arrow (
dataframeToArrow,
columnToArrow,
arrowToDataframe,
-- exported only to force linking of release-callback symbols
releaseSchemaImpl,
releaseArrayImpl,
) where
import qualified Data.ByteString as BS
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import qualified DataFrame.Internal.Column as DI
import Control.Monad (foldM_, forM, join, void, when, zipWithM_)
import Data.Bits (setBit, testBit)
import Data.Int (Int32, Int64)
import Data.Maybe (fromMaybe, isNothing)
import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl))
import Data.Word (Word8)
import Foreign hiding (void)
import Foreign.C.String (CString, newCString, peekCString)
import Type.Reflection (typeRep)
import DataFrame.Internal.Column (Column (..))
import DataFrame.Internal.DataFrame (DataFrame (..))
import DataFrame.Operations.Core (fromNamedColumns)
-- ---------------------------------------------------------------------------
-- Opaque phantom types for the Arrow structs
-- ---------------------------------------------------------------------------
data ArrowSchema
data ArrowArray
arrowSchemaSize :: Int
arrowSchemaSize = 72 -- 9 × 8 bytes
arrowArraySize :: Int
arrowArraySize = 80 -- 10 × 8 bytes
-- ArrowSchema field byte offsets
_schemaFormat
, _schemaName
, _schemaMetadata
, _schemaFlags
, _schemaNChildren
, _schemaChildren
, _schemaDictionary
, _schemaRelease
, _schemaPrivateData ::
Int
_schemaFormat = 0
_schemaName = 8
_schemaMetadata = 16
_schemaFlags = 24
_schemaNChildren = 32
_schemaChildren = 40
_schemaDictionary = 48
_schemaRelease = 56
_schemaPrivateData = 64
-- ArrowArray field byte offsets
_arrayLength
, _arrayNullCount
, _arrayOffset
, _arrayNBuffers
, _arrayNChildren
, _arrayBuffers
, _arrayChildren
, _arrayDictionary
, _arrayRelease
, _arrayPrivateData ::
Int
_arrayLength = 0
_arrayNullCount = 8
_arrayOffset = 16
_arrayNBuffers = 24
_arrayNChildren = 32
_arrayBuffers = 40
_arrayChildren = 48
_arrayDictionary = 56
_arrayRelease = 64
_arrayPrivateData = 72
-- ---------------------------------------------------------------------------
-- Helpers
-- ---------------------------------------------------------------------------
-- Write a Storable value at a byte offset from a base pointer.
at :: (Storable a) => Ptr b -> Int -> a -> IO ()
at p off = poke (castPtr (p `plusPtr` off))
-- Read a Storable value at a byte offset from a base pointer.
readAt :: (Storable a) => Ptr b -> Int -> IO a
readAt p off = peek (castPtr (p `plusPtr` off))
-- ---------------------------------------------------------------------------
-- Release callbacks (self-import trick for compile-time-constant FunPtr)
-- ---------------------------------------------------------------------------
foreign export ccall "df_release_schema"
releaseSchemaImpl :: Ptr ArrowSchema -> IO ()
foreign import ccall "&df_release_schema"
pReleaseSchema :: FunPtr (Ptr ArrowSchema -> IO ())
foreign export ccall "df_release_array"
releaseArrayImpl :: Ptr ArrowArray -> IO ()
foreign import ccall "&df_release_array"
pReleaseArray :: FunPtr (Ptr ArrowArray -> IO ())
-- Dynamic wrappers to call producer's release callbacks after copying.
foreign import ccall "dynamic"
callRelSchema :: FunPtr (Ptr ArrowSchema -> IO ()) -> Ptr ArrowSchema -> IO ()
foreign import ccall "dynamic"
callRelArray :: FunPtr (Ptr ArrowArray -> IO ()) -> Ptr ArrowArray -> IO ()
releaseSchemaImpl :: Ptr ArrowSchema -> IO ()
releaseSchemaImpl p = do
rawPriv <- peek (castPtr (p `plusPtr` _schemaPrivateData) :: Ptr (Ptr ()))
let sp = castPtrToStablePtr rawPriv :: StablePtr (IO ())
join (deRefStablePtr sp)
freeStablePtr sp
-- Arrow spec: release callback must set release to NULL to signal completion.
-- p here is Arrow C++'s internal copy of the struct (not our mallocBytes
-- allocation); our original allocation is freed inside the cleanup closure.
p `at` _schemaRelease $ (nullFunPtr :: FunPtr (Ptr ArrowSchema -> IO ()))
releaseArrayImpl :: Ptr ArrowArray -> IO ()
releaseArrayImpl p = do
rawPriv <- peek (castPtr (p `plusPtr` _arrayPrivateData) :: Ptr (Ptr ()))
let sp = castPtrToStablePtr rawPriv :: StablePtr (IO ())
join (deRefStablePtr sp)
freeStablePtr sp
-- Same reasoning as releaseSchemaImpl.
p `at` _arrayRelease $ (nullFunPtr :: FunPtr (Ptr ArrowArray -> IO ()))
makeLeafSchema :: String -> T.Text -> IO (Ptr ArrowSchema)
makeLeafSchema fmt colName = do
p <- mallocBytes arrowSchemaSize
fmtStr <- newCString fmt
nameStr <- newCString (T.unpack colName)
p `at` _schemaFormat $ fmtStr
p `at` _schemaName $ nameStr
p `at` _schemaMetadata $ (nullPtr :: Ptr ())
p `at` _schemaFlags $ (0 :: Int64)
p `at` _schemaNChildren $ (0 :: Int64)
p `at` _schemaChildren $ (nullPtr :: Ptr ())
p `at` _schemaDictionary $ (nullPtr :: Ptr ())
p `at` _schemaRelease $ pReleaseSchema
-- Capture p so our original mallocBytes allocation is freed when release runs.
cleanup <- newStablePtr (free fmtStr >> free nameStr >> free p)
p `at` _schemaPrivateData $ castStablePtrToPtr cleanup
return p
makeLeafArray :: Int -> Int64 -> [Ptr ()] -> IO () -> IO (Ptr ArrowArray)
makeLeafArray nRows nullCnt bufPtrs extraCleanup = do
p <- mallocBytes arrowArraySize
let nb = length bufPtrs
bufArr <- mallocArray nb :: IO (Ptr (Ptr ()))
zipWithM_ (pokeElemOff bufArr) [0 ..] bufPtrs
p `at` _arrayLength $ (fromIntegral nRows :: Int64)
p `at` _arrayNullCount $ nullCnt
p `at` _arrayOffset $ (0 :: Int64)
p `at` _arrayNBuffers $ (fromIntegral nb :: Int64)
p `at` _arrayNChildren $ (0 :: Int64)
p `at` _arrayBuffers $ bufArr
p `at` _arrayChildren $ (nullPtr :: Ptr ())
p `at` _arrayDictionary $ (nullPtr :: Ptr ())
p `at` _arrayRelease $ pReleaseArray
-- Capture p so our original mallocBytes allocation is freed when release runs.
cleanup <- newStablePtr (free bufArr >> extraCleanup >> free p)
p `at` _arrayPrivateData $ castStablePtrToPtr cleanup
return p
buildBitmap :: V.Vector (Maybe a) -> IO (Ptr Word8, Int)
buildBitmap vec = do
let n = V.length vec
bitmapBytes = max 1 ((n + 7) `div` 8)
nullCount = V.length (V.filter isNothing vec)
bitmapPtr <- mallocBytes bitmapBytes :: IO (Ptr Word8)
mapM_ (\i -> pokeElemOff bitmapPtr i (0 :: Word8)) [0 .. bitmapBytes - 1]
V.iforM_ vec $ \i mv ->
case mv of
Nothing -> return ()
Just _ -> do
let byteIdx = i `div` 8
bitIdx = i `mod` 8
b <- peekElemOff bitmapPtr byteIdx
pokeElemOff bitmapPtr byteIdx (setBit b bitIdx)
return (bitmapPtr, nullCount)
columnToArrow :: T.Text -> Column -> IO (Ptr ArrowSchema, Ptr ArrowArray)
columnToArrow colName (UnboxedColumn (vec :: VU.Vector a))
| Just Refl <- testEquality (typeRep @a) (typeRep @Int) = do
let n = VU.length vec
dataPtr <- mallocArray (max 1 n) :: IO (Ptr Int64)
VU.imapM_ (\i v -> pokeElemOff dataPtr i (fromIntegral v)) vec
sPtr <- makeLeafSchema "l" colName
aPtr <- makeLeafArray n 0 [nullPtr, castPtr dataPtr] (free dataPtr)
return (sPtr, aPtr)
columnToArrow colName (UnboxedColumn (vec :: VU.Vector a))
| Just Refl <- testEquality (typeRep @a) (typeRep @Double) = do
let n = VU.length vec
dataPtr <- mallocArray (max 1 n) :: IO (Ptr Double)
VU.imapM_ (pokeElemOff dataPtr) vec
sPtr <- makeLeafSchema "g" colName
aPtr <- makeLeafArray n 0 [nullPtr, castPtr dataPtr] (free dataPtr)
return (sPtr, aPtr)
columnToArrow colName (BoxedColumn (vec :: V.Vector a))
| Just Refl <- testEquality (typeRep @a) (typeRep @T.Text) = do
let n = V.length vec
bss = map TE.encodeUtf8 (V.toList vec)
cumOff = scanl (+) 0 (map BS.length bss)
total = last cumOff
offPtr <- mallocArray (n + 1) :: IO (Ptr Int32)
zipWithM_
(\i o -> pokeElemOff offPtr i (fromIntegral o :: Int32))
[0 ..]
cumOff
charsPtr <- mallocBytes (max 1 total) :: IO (Ptr Word8)
foldM_
( \pos bs -> do
BS.useAsCStringLen bs $ \(src, len) ->
copyBytes (charsPtr `plusPtr` pos) (castPtr src) len
return (pos + BS.length bs)
)
0
bss
sPtr <- makeLeafSchema "u" colName
aPtr <-
makeLeafArray
n
0
[nullPtr, castPtr offPtr, castPtr charsPtr]
(free offPtr >> free charsPtr)
return (sPtr, aPtr)
columnToArrow colName (BoxedColumn (vec :: V.Vector a))
| Just Refl <- testEquality (typeRep @a) (typeRep @Double) = do
let n = V.length vec
dataPtr <- mallocArray (max 1 n) :: IO (Ptr Double)
V.imapM_ (pokeElemOff dataPtr) vec
sPtr <- makeLeafSchema "g" colName
aPtr <- makeLeafArray n 0 [nullPtr, castPtr dataPtr] (free dataPtr)
return (sPtr, aPtr)
columnToArrow colName (BoxedColumn (vec :: V.Vector a))
| Just Refl <- testEquality (typeRep @a) (typeRep @Int) = do
let n = V.length vec
dataPtr <- mallocArray (max 1 n) :: IO (Ptr Int64)
V.imapM_ (\i v -> pokeElemOff dataPtr i (fromIntegral v)) vec
sPtr <- makeLeafSchema "l" colName
aPtr <- makeLeafArray n 0 [nullPtr, castPtr dataPtr] (free dataPtr)
return (sPtr, aPtr)
columnToArrow colName (OptionalColumn (vec :: V.Vector (Maybe a)))
| Just Refl <- testEquality (typeRep @a) (typeRep @Int) = do
let n = V.length vec
(bitmapPtr, nullCount) <- buildBitmap vec
dataPtr <- mallocArray (max 1 n) :: IO (Ptr Int64)
V.imapM_
( \i mv ->
pokeElemOff dataPtr i (fromIntegral (fromMaybe 0 mv) :: Int64)
)
vec
sPtr <- makeLeafSchema "l" colName
aPtr <-
makeLeafArray
n
(fromIntegral nullCount)
[castPtr bitmapPtr, castPtr dataPtr]
(free bitmapPtr >> free dataPtr)
return (sPtr, aPtr)
columnToArrow colName (OptionalColumn (vec :: V.Vector (Maybe a)))
| Just Refl <- testEquality (typeRep @a) (typeRep @Double) = do
let n = V.length vec
(bitmapPtr, nullCount) <- buildBitmap vec
dataPtr <- mallocArray (max 1 n) :: IO (Ptr Double)
V.imapM_
( \i mv ->
pokeElemOff dataPtr i (maybe 0.0 realToFrac mv :: Double)
)
vec
sPtr <- makeLeafSchema "g" colName
aPtr <-
makeLeafArray
n
(fromIntegral nullCount)
[castPtr bitmapPtr, castPtr dataPtr]
(free bitmapPtr >> free dataPtr)
return (sPtr, aPtr)
columnToArrow colName (OptionalColumn (vec :: V.Vector (Maybe a)))
| Just Refl <- testEquality (typeRep @a) (typeRep @T.Text) = do
let n = V.length vec
texts = V.toList vec
bss = map (maybe BS.empty TE.encodeUtf8) texts
cumOff = scanl (+) 0 (map BS.length bss)
total = last cumOff
(bitmapPtr, nullCount) <- buildBitmap vec
offPtr <- mallocArray (n + 1) :: IO (Ptr Int32)
zipWithM_
(\i o -> pokeElemOff offPtr i (fromIntegral o :: Int32))
[0 ..]
cumOff
charsPtr <- mallocBytes (max 1 total) :: IO (Ptr Word8)
foldM_
( \pos bs -> do
BS.useAsCStringLen bs $ \(src, len) ->
copyBytes (charsPtr `plusPtr` pos) (castPtr src) len
return (pos + BS.length bs)
)
0
bss
sPtr <- makeLeafSchema "u" colName
aPtr <-
makeLeafArray
n
(fromIntegral nullCount)
[castPtr bitmapPtr, castPtr offPtr, castPtr charsPtr]
(free bitmapPtr >> free offPtr >> free charsPtr)
return (sPtr, aPtr)
columnToArrow colName _ =
error $
"DataFrame.IO.Arrow.columnToArrow: unsupported column type for '"
++ T.unpack colName
++ "'"
dataframeToArrow :: DataFrame -> IO (Ptr ArrowSchema, Ptr ArrowArray)
dataframeToArrow df = do
let idxToName = M.fromList [(v, k) | (k, v) <- M.toList (columnIndices df)]
ncols = M.size (columnIndices df)
colsInOrder =
[ (idxToName M.! i, columns df V.! i)
| i <- [0 .. ncols - 1]
]
childPairs <- forM colsInOrder (uncurry columnToArrow)
let childSPtrs = map fst childPairs
childAPtrs = map snd childPairs
let nRows = case colsInOrder of
[] -> 0
_ -> DI.columnLength (snd (head colsInOrder))
topSchema <- mallocBytes arrowSchemaSize
fmtStr <- newCString "+s"
nameStr <- newCString ""
childSArr <- mallocArray ncols :: IO (Ptr (Ptr ArrowSchema))
zipWithM_ (pokeElemOff childSArr) [0 ..] childSPtrs
topSchema `at` _schemaFormat $ fmtStr
topSchema `at` _schemaName $ nameStr
topSchema `at` _schemaMetadata $ (nullPtr :: Ptr ())
topSchema `at` _schemaFlags $ (0 :: Int64)
topSchema `at` _schemaNChildren $ (fromIntegral ncols :: Int64)
topSchema `at` _schemaChildren $ childSArr
topSchema `at` _schemaDictionary $ (nullPtr :: Ptr ())
topSchema `at` _schemaRelease $ pReleaseSchema
-- Do NOT loop over children here: Arrow C++ zeroes children[i]->release
-- during import, so reading it would yield a null function pointer.
-- Children are released independently by Arrow C++; their own cleanup
-- closures free their buffers and struct memory.
cleanupS <- newStablePtr $ do
free childSArr
free fmtStr
free nameStr
free topSchema -- free our original mallocBytes allocation
topSchema `at` _schemaPrivateData $ castStablePtrToPtr cleanupS
-- ── Top-level struct array ──────────────────────────────────────────────
topArray <- mallocBytes arrowArraySize
childAArr <- mallocArray ncols :: IO (Ptr (Ptr ArrowArray))
zipWithM_ (pokeElemOff childAArr) [0 ..] childAPtrs
topBufArr <- mallocArray 1 :: IO (Ptr (Ptr ()))
pokeElemOff topBufArr 0 nullPtr
topArray `at` _arrayLength $ (fromIntegral nRows :: Int64)
topArray `at` _arrayNullCount $ (0 :: Int64)
topArray `at` _arrayOffset $ (0 :: Int64)
topArray `at` _arrayNBuffers $ (1 :: Int64)
topArray `at` _arrayNChildren $ (fromIntegral ncols :: Int64)
topArray `at` _arrayBuffers $ topBufArr
topArray `at` _arrayChildren $ childAArr
topArray `at` _arrayDictionary $ (nullPtr :: Ptr ())
topArray `at` _arrayRelease $ pReleaseArray
-- Same reasoning as cleanupS: Arrow C++ manages children independently.
cleanupA <- newStablePtr $ do
free childAArr
free topBufArr
free topArray -- free our original mallocBytes allocation
topArray `at` _arrayPrivateData $ castStablePtrToPtr cleanupA
return (topSchema, topArray)
-- | Test whether bit i is set in a validity bitmap.
bitmapIsSet :: Ptr Word8 -> Int -> IO Bool
bitmapIsSet bitmapPtr i =
testBit <$> peekElemOff bitmapPtr (i `div` 8) <*> pure (i `mod` 8)
{- | Import an Arrow RecordBatch from raw C Data Interface pointers.
Copies all data into GC-managed Haskell vectors, then calls the
producer's release callbacks.
-}
arrowToDataframe :: Ptr () -> Ptr () -> IO DataFrame
arrowToDataframe rawSchema rawArray = do
let schemaPtr = castPtr rawSchema :: Ptr ArrowSchema
arrayPtr = castPtr rawArray :: Ptr ArrowArray
nCols <- readAt schemaPtr _schemaNChildren :: IO Int64
childSArr <- readAt schemaPtr _schemaChildren :: IO (Ptr (Ptr ArrowSchema))
childAArr <- readAt arrayPtr _arrayChildren :: IO (Ptr (Ptr ArrowArray))
cols <- forM [0 .. fromIntegral nCols - 1] $ \i -> do
cs <- peekElemOff childSArr i
ca <- peekElemOff childAArr i
readArrowColumn cs ca
-- Call producer's release callbacks after all data has been copied.
relA <- readAt arrayPtr _arrayRelease :: IO (FunPtr (Ptr ArrowArray -> IO ()))
when (relA /= nullFunPtr) $ callRelArray relA arrayPtr
relS <-
readAt schemaPtr _schemaRelease :: IO (FunPtr (Ptr ArrowSchema -> IO ()))
when (relS /= nullFunPtr) $ callRelSchema relS schemaPtr
return $ fromNamedColumns cols
readArrowColumn :: Ptr ArrowSchema -> Ptr ArrowArray -> IO (T.Text, Column)
readArrowColumn schemaPtr arrayPtr = do
fmtStr <- (readAt schemaPtr _schemaFormat :: IO CString) >>= peekCString
nameStr <- (readAt schemaPtr _schemaName :: IO CString) >>= peekCString
let name = T.pack nameStr
len <- readAt arrayPtr _arrayLength :: IO Int64
nullCnt <- readAt arrayPtr _arrayNullCount :: IO Int64
bufArr <- readAt arrayPtr _arrayBuffers :: IO (Ptr (Ptr ()))
let n = fromIntegral len
col <- case fmtStr of
"l" -> readInt64Col n nullCnt bufArr
"i" -> readInt32Col n nullCnt bufArr
"g" -> readFloat64Col n nullCnt bufArr
"f" -> readFloat32Col n nullCnt bufArr
"U" -> readLargeUtf8Col n nullCnt bufArr
"u" -> readUtf8Col n nullCnt bufArr
_ ->
error $
"DataFrame.IO.Arrow.readArrowColumn: unsupported format '"
++ fmtStr
++ "' for column '"
++ nameStr
++ "'"
return (name, col)
readInt64Col :: Int -> Int64 -> Ptr (Ptr ()) -> IO Column
readInt64Col n nullCnt bufArr = do
bitmapVoid <- peekElemOff bufArr 0
dataVoid <- peekElemOff bufArr 1
let dataPtr = castPtr dataVoid :: Ptr Int64
if nullCnt > 0
then do
let bitmapPtr = castPtr bitmapVoid :: Ptr Word8
vec <- V.generateM n $ \i -> do
valid <- bitmapIsSet bitmapPtr i
if valid
then do
v <- peekElemOff dataPtr i
return (Just (fromIntegral v :: Int))
else return Nothing
return $ OptionalColumn (vec :: V.Vector (Maybe Int))
else do
vec <- VU.generateM n $ \i -> do
v <- peekElemOff dataPtr i
return (fromIntegral v :: Int)
return $ UnboxedColumn (vec :: VU.Vector Int)
readInt32Col :: Int -> Int64 -> Ptr (Ptr ()) -> IO Column
readInt32Col n nullCnt bufArr = do
bitmapVoid <- peekElemOff bufArr 0
dataVoid <- peekElemOff bufArr 1
let dataPtr = castPtr dataVoid :: Ptr Int32
if nullCnt > 0
then do
let bitmapPtr = castPtr bitmapVoid :: Ptr Word8
vec <- V.generateM n $ \i -> do
valid <- bitmapIsSet bitmapPtr i
if valid
then do
v <- peekElemOff dataPtr i
return (Just (fromIntegral v :: Int))
else return Nothing
return $ OptionalColumn (vec :: V.Vector (Maybe Int))
else do
vec <- VU.generateM n $ \i -> do
v <- peekElemOff dataPtr i
return (fromIntegral v :: Int)
return $ UnboxedColumn (vec :: VU.Vector Int)
readFloat64Col :: Int -> Int64 -> Ptr (Ptr ()) -> IO Column
readFloat64Col n nullCnt bufArr = do
bitmapVoid <- peekElemOff bufArr 0
dataVoid <- peekElemOff bufArr 1
let dataPtr = castPtr dataVoid :: Ptr Double
if nullCnt > 0
then do
let bitmapPtr = castPtr bitmapVoid :: Ptr Word8
vec <- V.generateM n $ \i -> do
valid <- bitmapIsSet bitmapPtr i
if valid
then Just <$> (peekElemOff dataPtr i :: IO Double)
else return Nothing
return $ OptionalColumn (vec :: V.Vector (Maybe Double))
else do
vec <- VU.generateM n (peekElemOff dataPtr)
return $ UnboxedColumn (vec :: VU.Vector Double)
readFloat32Col :: Int -> Int64 -> Ptr (Ptr ()) -> IO Column
readFloat32Col n nullCnt bufArr = do
bitmapVoid <- peekElemOff bufArr 0
dataVoid <- peekElemOff bufArr 1
let dataPtr = castPtr dataVoid :: Ptr Float
if nullCnt > 0
then do
let bitmapPtr = castPtr bitmapVoid :: Ptr Word8
vec <- V.generateM n $ \i -> do
valid <- bitmapIsSet bitmapPtr i
if valid
then do
v <- peekElemOff dataPtr i
return (Just (realToFrac v :: Double))
else return Nothing
return $ OptionalColumn (vec :: V.Vector (Maybe Double))
else do
vec <- VU.generateM n $ \i -> do
v <- peekElemOff dataPtr i
return (realToFrac v :: Double)
return $ UnboxedColumn (vec :: VU.Vector Double)
-- | Read a large_string (format "U") column with int64 offsets.
readLargeUtf8Col :: Int -> Int64 -> Ptr (Ptr ()) -> IO Column
readLargeUtf8Col n nullCnt bufArr = do
bitmapVoid <- peekElemOff bufArr 0
offsetVoid <- peekElemOff bufArr 1
charVoid <- peekElemOff bufArr 2
let offsetPtr = castPtr offsetVoid :: Ptr Int64
charPtr = castPtr charVoid :: Ptr Word8
if nullCnt > 0
then do
let bitmapPtr = castPtr bitmapVoid :: Ptr Word8
vec <- V.generateM n $ \i -> do
valid <- bitmapIsSet bitmapPtr i
if valid
then do
start <- fromIntegral <$> peekElemOff offsetPtr i
end <- fromIntegral <$> peekElemOff offsetPtr (i + 1)
bs <-
BS.packCStringLen
(castPtr (charPtr `plusPtr` start), end - start)
return $ Just (TE.decodeUtf8 bs)
else return Nothing
return $ OptionalColumn vec
else do
vec <- V.generateM n $ \i -> do
start <- fromIntegral <$> peekElemOff offsetPtr i
end <- fromIntegral <$> peekElemOff offsetPtr (i + 1)
bs <-
BS.packCStringLen
(castPtr (charPtr `plusPtr` start), end - start)
return $ TE.decodeUtf8 bs
return $ BoxedColumn vec
-- | Read a utf8 (format "u") column with int32 offsets.
readUtf8Col :: Int -> Int64 -> Ptr (Ptr ()) -> IO Column
readUtf8Col n nullCnt bufArr = do
bitmapVoid <- peekElemOff bufArr 0
offsetVoid <- peekElemOff bufArr 1
charVoid <- peekElemOff bufArr 2
let offsetPtr = castPtr offsetVoid :: Ptr Int32
charPtr = castPtr charVoid :: Ptr Word8
if nullCnt > 0
then do
let bitmapPtr = castPtr bitmapVoid :: Ptr Word8
vec <- V.generateM n $ \i -> do
valid <- bitmapIsSet bitmapPtr i
if valid
then do
start <- fromIntegral <$> peekElemOff offsetPtr i
end <- fromIntegral <$> peekElemOff offsetPtr (i + 1)
bs <-
BS.packCStringLen
(castPtr (charPtr `plusPtr` start), end - start)
return $ Just (TE.decodeUtf8 bs)
else return Nothing
return $ OptionalColumn vec
else do
vec <- V.generateM n $ \i -> do
start <- fromIntegral <$> peekElemOff offsetPtr i
end <- fromIntegral <$> peekElemOff offsetPtr (i + 1)
bs <-
BS.packCStringLen
(castPtr (charPtr `plusPtr` start), end - start)
return $ TE.decodeUtf8 bs
return $ BoxedColumn vec