简体   繁体   English

如何创建两个调用此外部库API的ByteStrings?

[英]How to create two ByteStrings calling this external library API?

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]);

This function randomly generates a secret key, computes the corresponding public key and puts the results in pk and sk . 该函数随机生成一个密钥,计算相应的公钥并将结果放入pksk

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 . 当刚刚返回一个ByteString我发现最简单的方法是使用create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString来自Data.ByteString.Internal create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString However, that function can't create two ByteStrings at the same time. 但是,该函数不能同时创建两个ByteStrings

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)

However, this doesn't seem to work with GHC 7.10.2. 但是,这似乎不适用于GHC 7.10.2。 When running the test suite I'm finding that I seem to have sharing of the ByteString s in between function calls, leading to encryption/decryption failing and giving incorrect results. 运行测试套件时,我发现我似乎在函数调用之间共享了ByteString ,导致加密/解密失败并给出不正确的结果。

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)

and using it like: 并使用它像:

randomKeypair = fmap (PublicKey *** SecretKey) $
    createWithResult publicKeyBytes $ \ppk ->
    B.create secretKeyBytes $ \psk ->
    void $ c_random_keypair ppk psk

This seems to work, all tests pass. 这似乎有效,所有测试都通过。

My question is, what exactly are the semantics when it comes to sharing and referential transparency when it comes to the IO monad? 我的问题是,当谈到IO monad时,在共享和引用透明度方面究竟是什么语义?

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. 我认为发生的事情是优化器看到let statement可以浮动到顶级定义中,这就是我遇到这些问题的原因。

This does not answer your question, but it's too long to be put in a comment. 这不能回答你的问题,但是要发表评论太久了。

As a hack, if you want to avoid doing manual allocations, you could use two nested create calls and an IORef ByteString to store the bytestring created by the innermost create . 作为一个hack,如果你想避免进行手动分配,你可以使用两个嵌套的create调用和一个IORef ByteString来存储由最里面的createIORef ByteString Eg (pseudocode) 例如(伪代码)

secRef <- newIORef ""
pubB <- create publicKeyBytes (\pub -> do
   secB <- create secretKeyBytes (\sec -> void $ c_random_keypair pub sec)
   writeIORef secRef secB)
secB <- readIORef secRef
return (pubB, secB)

However, I prefer your createWithResult to this approach. 但是,我更喜欢你的createWithResult这种方法。

The problem with your first approach is that you're trying to modify an immutable value ( pk and sk in your function). 您的第一种方法的问题是您正在尝试修改不可变值(函数中的pksk )。 The docs for unsafeUseAsCString say: unsafeUseAsCString文档说:

modifying the CString, either in C, or using poke, will cause the contents of the ByteString to change, breaking referential transparency 在C语言中修改CString或使用poke修改CString会导致ByteString的内容发生变化,从而破坏参照透明度

The IO monad doesn't have different semantics when it comes to sharing and referential transparency. 在共享和引用透明性方面, IO monad没有不同的语义。 In fact, the let in the do block is not in any way related to IO monad; 实际上, do块中的letIO 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)

Now it's clearly visible that pk and sk can be floated to top level. 现在可以清楚地看到pksk可以浮动到顶层。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM