packages feed

dataframe-parquet-1.5.1.0: src/DataFrame/IO/Parquet/Writer/DefLevels.hs

{-# LANGUAGE OverloadedRecordDot #-}

module DataFrame.IO.Parquet.Writer.DefLevels (
    DefLevels (..),
    newDefLevels,
    pushDef,
    flushDef,
) where

import Control.Monad (when)
import Data.Bits (shiftL, shiftR, (.&.), (.|.))
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Word (Word64)
import DataFrame.IO.Utils.RandomAccess (MemoryBuffer, mallocBuffer, writeWord8)

data DefLevels = DefLevels
    { dlBuf :: !MemoryBuffer
    , dlValue :: !(IORef Int)
    , dlCount :: !(IORef Int)
    }

newDefLevels :: IO DefLevels
newDefLevels = DefLevels <$> mallocBuffer 64 <*> newIORef 0 <*> newIORef 0

pushDef :: DefLevels -> Int -> IO ()
pushDef dl value = do
    count <- readIORef dl.dlCount
    if count == 0
        then writeIORef dl.dlValue value >> writeIORef dl.dlCount 1
        else do
            current <- readIORef dl.dlValue
            if current == value
                then writeIORef dl.dlCount (count + 1)
                else do
                    writeDefRun dl current count
                    writeIORef dl.dlValue value
                    writeIORef dl.dlCount 1
{-# INLINE pushDef #-}

flushDef :: DefLevels -> IO ()
flushDef dl = do
    count <- readIORef dl.dlCount
    when (count > 0) $ do
        value <- readIORef dl.dlValue
        writeDefRun dl value count
    writeIORef dl.dlCount 0
{-# INLINE flushDef #-}

writeDefRun :: DefLevels -> Int -> Int -> IO ()
writeDefRun dl value count = do
    writeLeb128 dl.dlBuf (fromIntegral (count `shiftL` 1))
    writeWord8 dl.dlBuf (fromIntegral value)
{-# INLINE writeDefRun #-}

writeLeb128 :: MemoryBuffer -> Word64 -> IO ()
writeLeb128 buffer value
    | value < 0x80 = writeWord8 buffer (fromIntegral value)
    | otherwise = do
        writeWord8 buffer (fromIntegral (value .&. 0x7f) .|. 0x80)
        writeLeb128 buffer (value `shiftR` 7)
{-# INLINE writeLeb128 #-}