dna*_*naq 5 haskell ffi bytestring
我目前正在编写绑定到加密库,该库公开了生成密钥对的函数:
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
Run Code Online (Sandbox Code Playgroud)
此函数随机生成一个密钥,计算相应的公钥并将结果放入pk和sk.
当刚刚返回一个ByteString我发现,最简单的方法是使用create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString从Data.ByteString.Internal.但是,该功能不能同时创建两个ByteStrings.
我的第一个方法是写下这样的东西:
newtype PublicKey = PublicKey ByteString
newtype SecretKey = SecretKey ByteString
randomKeypair :: IO (PublicKey, SecretKey)
randomKeypair = do
let pk = B.replicate 0 publicKeyBytes
sk = B.replicate 0 secretKeyBytes
B.unsafeUseAsCString pk $ \ppk ->
B.unsafeUseAsCString sk $ \psk ->
c_random_keypair ppk psk
return (PublicKey pk, SecretKey sk)
Run Code Online (Sandbox Code Playgroud)
但是,这似乎不适用于GHC 7.10.2.运行测试套件时,我发现我似乎ByteString在函数调用之间共享s,导致加密/解密失败并给出不正确的结果.
我设法通过定义自己的函数来解决这个问题:
createWithResult :: Int -> (Ptr Word8 -> IO a) -> IO (ByteString, a)
createWithResult i f = do
fp <- B.mallocByteString i
r <- withForeignPtr fp f
return (B.fromForeignPtr fp 0 i, r)
Run Code Online (Sandbox Code Playgroud)
并使用它像:
randomKeypair = fmap (PublicKey *** SecretKey) $
createWithResult publicKeyBytes $ \ppk ->
B.create secretKeyBytes $ \psk ->
void $ c_random_keypair ppk psk
Run Code Online (Sandbox Code Playgroud)
这似乎有效,所有测试都通过.
我的问题是,当谈到IOmonad 时,分享和引用透明度的语义究竟是什么?
我的直觉告诉我(错误地)我可以用第一种方式解决问题,但显然我做不到.我认为正在发生的事情是优化器看到let-statements可以浮出最高级定义,这就是我遇到这些问题的原因.
小智 2
第一种方法的问题是您试图修改一个不可变的值(pk以及sk在您的函数中)。unsafeUseAsCString 的文档说:
修改 CString(无论是在 C 中还是使用 poke)都会导致 ByteString 的内容发生更改,从而破坏引用透明度
在共享和引用透明度方面,单子IO没有不同的语义。事实上,块let中的与monaddo没有任何关系;IO你的代码相当于:
randomKeypair :: IO (PublicKey, SecretKey)
randomKeypair =
let pk = B.replicate 0 publicKeyBytes
sk = B.replicate 0 secretKeyBytes
in B.unsafeUseAsCString pk (\ppk ->
B.unsafeUseAsCString sk $ \psk ->
c_random_keypair ppk psk) >>
return (PublicKey pk, SecretKey sk)
Run Code Online (Sandbox Code Playgroud)
现在它清晰可见,pk并且sk可以浮动到顶层。