packages feed

clickhouse-haskell-0.1.1.0: src/Database/ClickHouseDriver/IO/BufferedWriter.hs

-- Copyright (c) 2014-present, EMQX, Inc.
-- All rights reserved.
--
-- This source code is distributed under the terms of a MIT license,
-- found in the LICENSE file.

{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE FlexibleInstances        #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE MultiParamTypeClasses    #-}
{-# LANGUAGE OverloadedStrings        #-}

-- | Tools to serialize data sent server. This module is for internal use only.

module Database.ClickHouseDriver.IO.BufferedWriter
  ( writeBinaryStr,
    writeBinaryFixedLengthStr,
    writeVarUInt,
    c_write_varint,
    writeBinaryInt8,
    writeBinaryInt16,
    writeBinaryInt32,
    writeBinaryInt64,
    writeBinaryUInt8,
    writeBinaryUInt16,
    writeBinaryUInt32,
    writeBinaryUInt64,
    writeBinaryUInt128,
    writeIn,
    transform,
    Writer,
    MonoidMap,
  )
where

import Control.Monad.IO.Class ( MonadIO(liftIO) )
import           Control.Monad.Writer         (WriterT, tell)
import qualified Data.Binary                  as Binary
import           Data.ByteString              (ByteString)
import qualified Data.ByteString              as BS
import Data.ByteString.Builder
    ( Builder, toLazyByteString, byteString )
import qualified Data.ByteString.Lazy         as L
import           Data.ByteString.Lazy.Builder (lazyByteString)
import Data.ByteString.Unsafe
    ( unsafePackCString, unsafePackCStringLen )
import           Data.DoubleWord              (Word128 (..))
import Data.Int ( Int8, Int16, Int32, Int64 )
import Data.Word ( Word8, Word16, Word32, Word64 )
import Foreign.C ( CString )

-- Monoid Homomorphism.
class (Monoid w, Monoid m)=>MonoidMap w m where
  transform :: w->m

instance MonoidMap ByteString L.ByteString where
  transform = L.fromStrict

instance MonoidMap L.ByteString ByteString where
  transform = L.toStrict

instance MonoidMap ByteString Builder where
  transform = byteString

instance MonoidMap L.ByteString Builder where
  transform = lazyByteString

instance MonoidMap Builder L.ByteString where
  transform = toLazyByteString

instance MonoidMap Builder ByteString where
  transform = L.toStrict . toLazyByteString

instance (Monoid w)=>MonoidMap w w where
  transform = id

-- | The writer monad writes bytestring builders and combine them as a monoid. 
type Writer w = WriterT w IO ()

writeBinaryFixedLengthStr :: (MonoidMap ByteString w)=>Word->ByteString->Writer w
writeBinaryFixedLengthStr len str = do
  let l = fromIntegral $ BS.length str
  if len /= l
    then error "Error: the length of the given bytestring does not equal to the given length"
    else do
      writeIn str

writeBinaryStr :: (MonoidMap ByteString w)=>ByteString->Writer w
writeBinaryStr str = do
  let l = BS.length str
  writeVarUInt (fromIntegral l)
  writeIn str

writeVarUInt ::(MonoidMap ByteString w)=>Word->Writer w
writeVarUInt n = do
   varuint <- liftIO $ leb128 n
   writeIn varuint 
   where
      leb128 :: Word->IO ByteString
      leb128 0 = do
        ostr' <- c_write_varint 0
        unsafePackCStringLen (ostr', 1)
      leb128 n = do
        ostr' <- c_write_varint n
        unsafePackCString ostr'

writeBinaryUInt8 :: (MonoidMap L.ByteString w)=>Word8->Writer w
writeBinaryUInt8 = tell . transform . L.reverse . Binary.encode

writeBinaryInt8 :: (MonoidMap L.ByteString w)=>Int8->Writer w
writeBinaryInt8 = tell . transform . L.reverse . Binary.encode

writeBinaryInt16 :: (MonoidMap L.ByteString w)=>Int16->Writer w
writeBinaryInt16 = tell . transform . L.reverse . Binary.encode

writeBinaryInt32 :: (MonoidMap L.ByteString w)=>Int32->Writer w
writeBinaryInt32 = tell . transform . L.reverse . Binary.encode

writeBinaryInt64 :: (MonoidMap L.ByteString w)=>Int64->Writer w
writeBinaryInt64 = tell . transform . L.reverse . Binary.encode

writeBinaryUInt16 :: (MonoidMap L.ByteString w)=>Word16->Writer w
writeBinaryUInt16 = tell . transform . L.reverse . Binary.encode

writeBinaryUInt32 :: (MonoidMap L.ByteString w)=>Word32->Writer w
writeBinaryUInt32 = tell . transform . L.reverse . Binary.encode

writeBinaryUInt64 :: (MonoidMap L.ByteString w)=>Word64->Writer w
writeBinaryUInt64 = tell . transform . L.reverse . Binary.encode

writeBinaryUInt128 :: (MonoidMap L.ByteString w)=>Word128->Writer w
writeBinaryUInt128 (Word128 hi lo) = do
  writeBinaryUInt64 hi
  writeBinaryUInt64 lo

writeIn :: (MonoidMap m w)=>m->Writer w
writeIn = tell . transform

foreign import ccall unsafe "varuint.h write_varint" c_write_varint :: Word -> IO CString