wireform-core-0.3.0.0: test/Wireform/FFI/Test.hs
module Wireform.FFI.Test (spec) where
import Data.ByteString qualified as BS
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Marshal.Array (peekArray)
import Foreign.Marshal.Utils (fillBytes)
import Foreign.Ptr (nullPtr)
import Test.Syd
import Wireform.Encode.Direct qualified as Direct
import Wireform.FFI (compareBounds, validateArrowBuffers)
import Wireform.Hash qualified as Hash
{- | Negative counts and lengths must be rejected at the wrapper, not
widened: they cross the FFI as 'Data.Word.CSize'/'Int64', so a negative
'Int' would become a huge count and make the kernel read past the
caller's buffers.
-}
spec :: Spec
spec = describe "Wireform.FFI and direct encoding boundaries" $ do
it "rejects a negative buffer count" $ do
validateArrowBuffers nullPtr (-1) 8 `shouldBe` False
it "rejects a negative body length" $ do
validateArrowBuffers nullPtr 0 (-1) `shouldBe` False
validateArrowBuffers nullPtr 1 (-1) `shouldBe` False
it "rejects a negative bounds count with an empty mask" $ do
compareBounds nullPtr (-1) 4 nullPtr `shouldBe` 0
it "rejects a negative bound width with an empty mask" $ do
compareBounds nullPtr 1 (-1) nullPtr `shouldBe` 0
it "still accepts empty input" $ do
validateArrowBuffers nullPtr 0 0 `shouldBe` True
compareBounds nullPtr 0 0 nullPtr `shouldBe` 0
it "keeps IO XXH64 byte-identical for empty, sliced, and block-boundary inputs" $ do
empty <- Hash.xxh64IO 0 BS.empty
empty `shouldBe` 0xef46db3751d8e999
abc <- Hash.xxh64IO 0 (BS.pack [97, 98, 99])
abc `shouldBe` 0x44bc2cf5ad770999
mapM_
( \size -> do
let payload = BS.pack (take size (cycle [0 .. 255]))
sliced = BS.take size (BS.drop 3 (BS.replicate 3 0xa5 <> payload <> BS.replicate 7 0x5a))
mapM_
(\seed -> Hash.xxh64IO seed sliced >>= (`shouldBe` Hash.xxh64 seed payload))
[0, 1, 0x123456789abcdef0, maxBound]
)
[0, 1, 7, 8, 15, 16, 31, 32, 33, 63, 64, 65, 257]
it "writes raw varints across the 21-bit boundary without a field tag" $ do
-- The published fallback used the field encoder with tag zero. That
-- inserted an extra byte for values >= 2^21 (including 2 MiB lengths).
let check value expected = allocaBytes 16 $ \ptr -> do
fillBytes ptr 0xa5 16
end <- Direct.dVarint ptr 3 value
bytes <- peekArray 16 ptr
end `shouldBe` (3 + length expected)
bytes `shouldBe` (replicate 3 0xa5 ++ expected ++ replicate (13 - length expected) 0xa5)
mapM_
(uncurry check)
[ (0x1fffff, [255, 255, 127])
, (0x200000, [128, 128, 128, 1])
, (0xfffffff, [255, 255, 255, 127])
, (0x10000000, [128, 128, 128, 128, 1])
, (maxBound, replicate 9 255 ++ [1])
]