{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Codec.CBOR.Magic
(
grabWord8
, grabWord16
, grabWord32
, grabWord64
, eatTailWord8
, eatTailWord16
, eatTailWord32
, eatTailWord64
, wordToFloat16
, floatToWord16
, wordToFloat32
, wordToFloat64
, nintegerFromBytes
, uintegerFromBytes
, Counter
, newCounter
, readCounter
, writeCounter
, incCounter
, decCounter
, copyByteStringToByteArray
, copyByteArrayToByteString
) where
#include "cbor.h"
import GHC.Exts
import GHC.ST (ST(ST))
import GHC.IO (IO(IO), unsafeDupablePerformIO)
import GHC.Word
import Foreign.Ptr
#if defined(OPTIMIZE_GMP)
import qualified GHC.Integer.GMP.Internals as Gmp
#endif
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BS
import qualified Data.ByteString.Unsafe as BS
import Data.Primitive.ByteArray as Prim
import Foreign.ForeignPtr (withForeignPtr)
import qualified Numeric.Half as Half
#if !defined(HAVE_BYTESWAP_PRIMOPS) || !defined(MEM_UNALIGNED_OPS)
import Data.Bits ((.|.), unsafeShiftL)
#if defined(ARCH_32bit)
import GHC.IntWord64 (wordToWord64#)
#endif
#endif
grabWord8 :: Ptr () -> Word
{-# INLINE grabWord8 #-}
grabWord16 :: Ptr () -> Word
{-# INLINE grabWord16 #-}
grabWord32 :: Ptr () -> Word
{-# INLINE grabWord32 #-}
grabWord64 :: Ptr () -> Word64
{-# INLINE grabWord64 #-}
grabWord8 (Ptr ip#) = W# (indexWord8OffAddr# ip# 0#)
#if defined(HAVE_BYTESWAP_PRIMOPS) && \
defined(MEM_UNALIGNED_OPS) && \
!defined(WORDS_BIGENDIAN)
grabWord16 (Ptr ip#) = W# (narrow16Word# (byteSwap16# (indexWord16OffAddr# ip# 0#)))
grabWord32 (Ptr ip#) = W# (narrow32Word# (byteSwap32# (indexWord32OffAddr# ip# 0#)))
#if defined(ARCH_64bit)
grabWord64 (Ptr ip#) = W64# (byteSwap# (indexWord64OffAddr# ip# 0#))
#else
grabWord64 (Ptr ip#) = W64# (byteSwap64# (indexWord64OffAddr# ip# 0#))
#endif
#elif defined(MEM_UNALIGNED_OPS) && \
defined(WORDS_BIGENDIAN)
grabWord16 (Ptr ip#) = W# (indexWord16OffAddr# ip# 0#)
grabWord32 (Ptr ip#) = W# (indexWord32OffAddr# ip# 0#)
grabWord64 (Ptr ip#) = W64# (indexWord64OffAddr# ip# 0#)
#else
grabWord16 (Ptr ip#) =
case indexWord8OffAddr# ip# 0# of
w0# ->
case indexWord8OffAddr# ip# 1# of
w1# -> W# w0# `unsafeShiftL` 8 .|.
W# w1#
grabWord32 (Ptr ip#) =
case indexWord8OffAddr# ip# 0# of
w0# ->
case indexWord8OffAddr# ip# 1# of
w1# ->
case indexWord8OffAddr# ip# 2# of
w2# ->
case indexWord8OffAddr# ip# 3# of
w3# -> W# w0# `unsafeShiftL` 24 .|.
W# w1# `unsafeShiftL` 16 .|.
W# w2# `unsafeShiftL` 8 .|.
W# w3#
grabWord64 (Ptr ip#) =
case indexWord8OffAddr# ip# 0# of
w0# ->
case indexWord8OffAddr# ip# 1# of
w1# ->
case indexWord8OffAddr# ip# 2# of
w2# ->
case indexWord8OffAddr# ip# 3# of
w3# ->
case indexWord8OffAddr# ip# 4# of
w4# ->
case indexWord8OffAddr# ip# 5# of
w5# ->
case indexWord8OffAddr# ip# 6# of
w6# ->
case indexWord8OffAddr# ip# 7# of
w7# -> w w0# `unsafeShiftL` 56 .|.
w w1# `unsafeShiftL` 48 .|.
w w2# `unsafeShiftL` 40 .|.
w w3# `unsafeShiftL` 32 .|.
w w4# `unsafeShiftL` 24 .|.
w w5# `unsafeShiftL` 16 .|.
w w6# `unsafeShiftL` 8 .|.
w w7#
where
#if defined(ARCH_64bit)
w w# = W64# w#
#else
w w# = W64# (wordToWord64# w#)
#endif
#endif
eatTailWord8 :: ByteString -> Word
eatTailWord8 xs = withBsPtr grabWord8 (BS.unsafeTail xs)
{-# INLINE eatTailWord8 #-}
eatTailWord16 :: ByteString -> Word
eatTailWord16 xs = withBsPtr grabWord16 (BS.unsafeTail xs)
{-# INLINE eatTailWord16 #-}
eatTailWord32 :: ByteString -> Word
eatTailWord32 xs = withBsPtr grabWord32 (BS.unsafeTail xs)
{-# INLINE eatTailWord32 #-}
eatTailWord64 :: ByteString -> Word64
eatTailWord64 xs = withBsPtr grabWord64 (BS.unsafeTail xs)
{-# INLINE eatTailWord64 #-}
withBsPtr :: (Ptr b -> a) -> ByteString -> a
withBsPtr f (BS.PS x off _) =
#if MIN_VERSION_bytestring(0,10,6)
BS.accursedUnutterablePerformIO $ withForeignPtr x $
\(Ptr addr#) -> return $! (f (Ptr addr# `plusPtr` off))
#else
unsafeDupablePerformIO $ withForeignPtr x $
\(Ptr addr#) -> return $! (f (Ptr addr# `plusPtr` off))
#endif
{-# INLINE withBsPtr #-}
wordToFloat16 :: Word -> Float
wordToFloat16 = \x -> Half.fromHalf (Half.Half (fromIntegral x))
{-# INLINE wordToFloat16 #-}
floatToWord16 :: Float -> Word16
floatToWord16 = \x -> fromIntegral (Half.getHalf (Half.toHalf x))
{-# INLINE floatToWord16 #-}
wordToFloat32 :: Word -> Float
wordToFloat32 (W# w#) = F# (wordToFloat32# w#)
{-# INLINE wordToFloat32 #-}
wordToFloat64 :: Word64 -> Double
wordToFloat64 (W64# w#) = D# (wordToFloat64# w#)
{-# INLINE wordToFloat64 #-}
wordToFloat32# :: Word# -> Float#
wordToFloat32# w# =
case newByteArray# 4# realWorld# of
(# s', mba# #) ->
case writeWord32Array# mba# 0# w# s' of
s'' ->
case readFloatArray# mba# 0# s'' of
(# _, f# #) -> f#
{-# NOINLINE wordToFloat32# #-}
#if defined(ARCH_64bit)
wordToFloat64# :: Word# -> Double#
#else
wordToFloat64# :: Word64# -> Double#
#endif
wordToFloat64# w# =
case newByteArray# 8# realWorld# of
(# s', mba# #) ->
case writeWord64Array# mba# 0# w# s' of
s'' ->
case readDoubleArray# mba# 0# s'' of
(# _, f# #) -> f#
{-# NOINLINE wordToFloat64# #-}
nintegerFromBytes :: BS.ByteString -> Integer
nintegerFromBytes bs = -1 - uintegerFromBytes bs
uintegerFromBytes :: BS.ByteString -> Integer
#if defined(OPTIMIZE_GMP)
uintegerFromBytes (BS.PS fp (I# off#) (I# len#)) =
unsafeDupablePerformIO $
withForeignPtr fp $ \(Ptr addr#) ->
let addrOff# = addr# `plusAddr#` off#
in Gmp.importIntegerFromAddr addrOff# (int2Word# len#) 1#
#else
uintegerFromBytes bs =
case BS.uncons bs of
Nothing -> 0
Just (w0, ws0) -> go (fromIntegral w0) ws0
where
go !acc ws =
case BS.uncons ws of
Nothing -> acc
Just (w, ws') -> go (acc `unsafeShiftL` 8 + fromIntegral w) ws'
#endif
data Counter s = Counter (MutableByteArray# s)
newCounter :: Int -> ST s (Counter s)
newCounter (I# n#) =
ST (\s ->
case newByteArray# 8# s of
(# s', mba# #) ->
case writeIntArray# mba# 0# n# s' of
s'' -> (# s'', Counter mba# #))
{-# INLINE newCounter #-}
readCounter :: Counter s -> ST s Int
readCounter (Counter mba#) =
ST (\s ->
case readIntArray# mba# 0# s of
(# s', n# #) -> (# s', I# n# #))
{-# INLINE readCounter #-}
writeCounter :: Counter s -> Int -> ST s ()
writeCounter (Counter mba#) (I# n#) =
ST (\s ->
case writeIntArray# mba# 0# n# s of
s' -> (# s', () #))
{-# INLINE writeCounter #-}
incCounter :: Counter s -> ST s ()
incCounter c = do
x <- readCounter c
writeCounter c (x+1)
{-# INLINE incCounter #-}
decCounter :: Counter s -> ST s ()
decCounter c = do
x <- readCounter c
writeCounter c (x-1)
{-# INLINE decCounter #-}
copyByteStringToByteArray :: BS.ByteString -> Prim.ByteArray
copyByteStringToByteArray (BS.PS fp off len) =
unsafeDupablePerformIO $
withForeignPtr fp $ \ptr -> do
mba <- Prim.newByteArray len
copyPtrToMutableByteArray (ptr `plusPtr` off) mba 0 len
Prim.unsafeFreezeByteArray mba
copyByteArrayToByteString :: Prim.ByteArray
-> Int
-> Int
-> BS.ByteString
copyByteArrayToByteString ba off len =
unsafeDupablePerformIO $ do
fp <- BS.mallocByteString len
withForeignPtr fp $ \ptr -> do
copyByteArrayToPtr ba off ptr len
return (BS.PS fp 0 len)
copyPtrToMutableByteArray :: Ptr a
-> MutableByteArray RealWorld
-> Int
-> Int
-> IO ()
copyPtrToMutableByteArray (Ptr addr#) (MutableByteArray mba#) (I# off#) (I# len#) =
IO (\s ->
case copyAddrToByteArray# addr# mba# off# len# s of
s' -> (# s', () #))
copyByteArrayToPtr :: ByteArray
-> Int
-> Ptr a
-> Int
-> IO ()
copyByteArrayToPtr (ByteArray ba#) (I# off#) (Ptr addr#) (I# len#) =
IO (\s ->
case copyByteArrayToAddr# ba# off# addr# len# s of
s' -> (# s', () #))