rust: Enable C++ streams to be passed into Rust code

This commit is contained in:
Jack Grigg 2021-06-04 14:42:55 +01:00
parent 22d97339b2
commit e6dd9550e1
4 changed files with 121 additions and 0 deletions

View File

@ -0,0 +1,21 @@
// Copyright (c) 2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_STREAMS_H
#define ZCASH_RUST_INCLUDE_RUST_STREAMS_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef long (*read_callback_t)(void* context, unsigned char* pch, size_t nSize);
typedef long (*write_callback_t)(void* context, const unsigned char* pch, size_t nSize);
#ifdef __cplusplus
}
#endif
#endif // ZCASH_RUST_INCLUDE_RUST_STREAMS_H

View File

@ -66,6 +66,7 @@ use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree}
mod blake2b;
mod ed25519;
mod metrics_ffi;
mod streams_ffi;
mod tracing_ffi;
#[cfg(test)]

View File

@ -0,0 +1,54 @@
use std::io;
use std::ptr::NonNull;
use libc::{c_long, c_void, size_t};
pub type StreamObj = NonNull<c_void>;
pub type ReadCb =
unsafe extern "C" fn(obj: Option<StreamObj>, pch: *mut u8, size: size_t) -> c_long;
pub type WriteCb =
unsafe extern "C" fn(obj: Option<StreamObj>, pch: *const u8, size: size_t) -> c_long;
pub struct CppStreamReader {
inner: Option<StreamObj>,
cb: ReadCb,
}
impl CppStreamReader {
pub fn from_raw_parts(inner: Option<StreamObj>, cb: ReadCb) -> Self {
CppStreamReader { inner, cb }
}
}
impl io::Read for CppStreamReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match unsafe { (self.cb)(self.inner, buf.as_mut_ptr(), buf.len()) } {
-1 => Err(io::Error::new(io::ErrorKind::Other, "C++ stream error")),
n => Ok(n as usize),
}
}
}
pub struct CppStreamWriter {
inner: Option<StreamObj>,
cb: WriteCb,
}
impl CppStreamWriter {
pub fn from_raw_parts(inner: Option<StreamObj>, cb: WriteCb) -> Self {
CppStreamWriter { inner, cb }
}
}
impl io::Write for CppStreamWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match unsafe { (self.cb)(self.inner, buf.as_ptr(), buf.len()) } {
-1 => Err(io::Error::new(io::ErrorKind::Other, "C++ stream error")),
n => Ok(n as usize),
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

View File

@ -22,6 +22,51 @@
#include <utility>
#include <vector>
/**
* Wrapper around C++ stream objects, enabling them to be passed into Rust code.
*/
template<typename Stream>
class RustStream {
Stream* stream;
public:
RustStream(Stream& stream_) : stream(&stream_) {}
static long read_callback(void* context, unsigned char* pch, size_t nSize)
{
return reinterpret_cast<RustStream*>(context)->read(
reinterpret_cast<char*>(pch), nSize);
}
static long write_callback(void* context, const unsigned char* pch, size_t nSize)
{
return reinterpret_cast<RustStream*>(context)->write(
reinterpret_cast<const char*>(pch), nSize);
}
long read(char* pch, size_t nSize)
{
try {
stream->read(pch, nSize);
return nSize;
} catch (std::ios_base::failure e) {
// TODO: log
return -1;
}
}
long write(const char* pch, size_t nSize)
{
try {
stream->write(pch, nSize);
return nSize;
} catch (std::ios_base::failure e) {
// TODO: log
return -1;
}
}
};
template<typename Stream>
class OverrideStream
{