【发布时间】:2015-11-18 19:14:51
【问题描述】:
我目前正在编写与加密库的绑定,该库公开了用于生成密钥对的函数:
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
该函数随机生成一个秘钥,计算对应的公钥,并将结果放入pk和sk。
当只返回一个ByteString 时,我发现最简单的方法是使用Data.ByteString.Internal 中的create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString。但是,该函数不能同时创建两个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)
但是,这似乎不适用于 GHC 7.10.2。在运行测试套件时,我发现我似乎在函数调用之间共享了 ByteStrings,导致加密/解密失败并给出不正确的结果。
我已经通过定义自己的函数来解决这个问题:
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)
并像这样使用它:
randomKeypair = fmap (PublicKey *** SecretKey) $
createWithResult publicKeyBytes $ \ppk ->
B.create secretKeyBytes $ \psk ->
void $ c_random_keypair ppk psk
这似乎有效,所有测试都通过了。
我的问题是,对于 IO monad 而言,共享和引用透明的语义到底是什么?
我的直觉(错误地)告诉我,我可以用第一种方式解决问题,但显然我做不到。我相信正在发生的事情是优化器看到 let-statements 可以浮动到顶级定义中,这就是我遇到这些问题的原因。
【问题讨论】:
标签: haskell ffi bytestring