如何创建两个调用此外部库API的ByteString? [英] How to create two ByteStrings calling this external library API?

查看:93
本文介绍了如何创建两个调用此外部库API的ByteString?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在将绑定写入一个加密库,该库公开了用于生成密钥对的函数:

I'm currently writing bindings to a cryptographic library that exposes a function for generating keypairs:

const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
                   unsigned char sk[SECRETKEYBYTES]);

此函数随机生成一个秘密密钥,计算相应的公共密钥,并将结果放入pksk.

This function randomly generates a secret key, computes the corresponding public key and puts the results in pk and sk.

仅返回一个ByteString时,我发现最简单的方法是使用Data.ByteString.Internal中的create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString.但是,该函数不能同时创建两个ByteStrings.

When just returning one ByteString I've found that the easiest way is to use create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString from Data.ByteString.Internal. However, that function can't create two ByteStrings at the same time.

我的第一种方法是编写如下内容:

My first approach was to write something like:

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.运行测试套件时,我发现函数调用之间似乎共享了ByteString,导致加密/解密失败并给出错误的结果.

However, this doesn't seem to work with GHC 7.10.2. When running the test suite I'm finding that I seem to have sharing of the ByteStrings in between function calls, leading to encryption/decryption failing and giving incorrect results.

我已经通过定义自己的功能来解决此问题:

I've managed to work around the problem by defining my own function:

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

这似乎可行,所有测试均通过.

This seems to work, all tests pass.

我的问题是,关于IO monad的共享语义和参照透明性到底是什么?

My question is, what exactly are the semantics when it comes to sharing and referential transparency when it comes to the IO monad?

我的直觉告诉我(错误地)我可以用第一种方法解决问题,但显然我做不到.我认为发生的事情是优化程序发现let语句可以浮现到顶级定义中,这就是我遇到这些问题的原因.

My intuition told me (incorrectly) that I could solve the problem in the first way, but apparently I couldn't. What I believe was happening is that the optimizer saw that the let-statements could be floated up into top level definitions, and that was the reason I got these issues.

推荐答案

第一种方法的问题是,您试图修改不可变值(函数中的pksk). docs for unsafeUseAsCString 说:

The problem with your first approach is that you're trying to modify an immutable value (pk and sk in your function). The docs for unsafeUseAsCString say:

使用C或使用戳戳修改CString,将导致ByteString的内容发生更改,从而破坏引用透明性

modifying the CString, either in C, or using poke, will cause the contents of the ByteString to change, breaking referential transparency

IO monad在共享和引用透明性方面没有不同的语义.实际上,do块中的letIO monad无关.您的代码等同于:

The IO monad doesn't have different semantics when it comes to sharing and referential transparency. In fact, the let in the do block is not in any way related to IO monad; your code is equivalent to:

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)

现在可以清楚地看到pksk可以浮到顶层了.

Now it's clearly visible that pk and sk can be floated to top level.

这篇关于如何创建两个调用此外部库API的ByteString?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆