如何创建两个调用此外部库 API 的 ByteString?
How to create two ByteStrings calling this external library API?
我目前正在编写绑定到公开生成密钥对的函数的加密库:
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
该函数随机生成一个秘钥,计算出对应的public秘钥并将结果放入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。当 运行 测试套件时,我发现我似乎在函数调用之间共享 ByteString
,导致 encryption/decryption 失败并给出不正确的结果。
我已经通过定义自己的函数解决了这个问题:
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
语句可以浮动到顶级定义中,这就是我遇到这些问题的原因。
这并没有回答你的问题,但它太长了,无法发表评论。
作为 hack,如果你想避免手动分配,你可以使用两个嵌套的 create
调用和一个 IORef ByteString
来存储最里面的 create
创建的字节串。例如。 (伪代码)
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)
不过,我更喜欢你的 createWithResult
而不是这种方法。
第一种方法的问题是您试图修改不可变值(函数中的 pk
和 sk
)。 docs for unsafeUseAsCString 说:
modifying the CString, either in C, or using poke, will cause the contents of the ByteString to change, breaking referential transparency
IO
monad 在共享和引用透明性方面没有不同的语义。事实上,do
块中的 let
与 IO
monad 没有任何关系;您的代码相当于:
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)
现在可以清楚地看到pk
和sk
可以浮动到顶层了。
我目前正在编写绑定到公开生成密钥对的函数的加密库:
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
该函数随机生成一个秘钥,计算出对应的public秘钥并将结果放入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。当 运行 测试套件时,我发现我似乎在函数调用之间共享 ByteString
,导致 encryption/decryption 失败并给出不正确的结果。
我已经通过定义自己的函数解决了这个问题:
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
语句可以浮动到顶级定义中,这就是我遇到这些问题的原因。
这并没有回答你的问题,但它太长了,无法发表评论。
作为 hack,如果你想避免手动分配,你可以使用两个嵌套的 create
调用和一个 IORef ByteString
来存储最里面的 create
创建的字节串。例如。 (伪代码)
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)
不过,我更喜欢你的 createWithResult
而不是这种方法。
第一种方法的问题是您试图修改不可变值(函数中的 pk
和 sk
)。 docs for unsafeUseAsCString 说:
modifying the CString, either in C, or using poke, will cause the contents of the ByteString to change, breaking referential transparency
IO
monad 在共享和引用透明性方面没有不同的语义。事实上,do
块中的 let
与 IO
monad 没有任何关系;您的代码相当于:
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)
现在可以清楚地看到pk
和sk
可以浮动到顶层了。