Closed waynenilsen closed 9 years ago
Could you say what it is you're trying to do?
If you're trying to read a serialized message, you probably want capnp::serialize::read_message
or capnp::serialize_packed::read_message
I must admit I'm having a little trouble figuring out how to return the message once it's parsed, due to lifetime issues, but if you just want to print it that shouldn't be a problem.
I'm trying to interface between rust and python with a nice common binary format. Basically I need something that takes &[u8]
and returns the message object (if possible).
I couldn't quite figure out how to get this working with read_message
.
Basically I need something that takes &[u8] and returns the message object (if possible).
Unfortunately, we don't yet have a function that can conveniently do this without copying the data. We had some discussion and made some progress on this on issue #25.
If you're willing to copy the data, then capnp::serialize::read_message()
should work fine.
I couldn't quite figure out how to get this working with read_message.
Something like this should work:
fn foo(mut buffer: &[u8]) -> capnp::Result<capnp::OwnedSpaceMessageReader> {
capnp::serialize::read_message(&mut buffer, capnp::ReaderOptions::new())
}
Oh, I think you mean
capnp::serialize::read_message(&mut Cursor::new(&mut buffer), capnp::ReaderOptions::new())
since read_message
requires a &mut Read
.
Also, is there a way to return a date::Reader
(to go with the OP's example) or at least some sort of less dynamically-typed MessageReader? The only way I've found so far to return a specific type of Reader
, is to take a &mut MallocMessageBuilder
and copy it into there.
Eg:
let message_reader = try!(capnp::serialize_packed::read_message(&mut io::Cursor::new(bytes), capnp::message::DEFAULT_READER_OPTIONS));
let reader : try!(date::Reader = message_reader.get_root());
try!(message_builder.set_root(reader));
Ok(try!(message.get_root::<date::Builder>()).as_reader())
Also, is there a way to return a date::Reader (to go with the OP's example) or at least some sort of less dynamically-typed MessageReader?
One way to go about this might be to implement a container type, like this
pub struct OwnedMessage<T> {
phantom_data: ::std::marker::PhantomData<T>,
message: ::capnp::OwnedSpaceMessageReader,
}
impl <'a, T> OwnedMessage <T> where T: ::capnp::traits::FromPointerReader<'a> {
pub fn get(&'a self) -> ::capnp::Result<T> {
use capnp::MessageReader;
self.message.get_root()
}
}
You could then return a OwnedMessage<date::Reader>
from foo()
.
Thanks for the suggestions! Oddly this compiles
fn foo(mut buffer: &[u8]) -> capnp::Result<capnp::OwnedSpaceMessageReader> {
capnp::serialize::read_message(&mut buffer, capnp::ReaderOptions::new())
}
and this does not
fn foo(mut buffer: &[u8]) -> capnp::Result<capnp::OwnedSpaceMessageReader> {
capnp::serialize::read_message(&mut std::io::Cursor::new(&mut buffer), capnp::ReaderOptions::new())
}
with message
cargo build --release (in directory: /home/wayne/projects/rust-py-test/src)
Compiling py-test v0.1.0 (file:///home/wayne/projects/rust-py-test)
lib.rs:63:4: 63:34 error: the trait `std::io::Read` is not implemented for the type `std::io::cursor::Cursor<&mut &[u8]>` [E0277]
lib.rs:63 capnp::serialize::read_message(&mut std::io::Cursor::new(&mut buffer), capnp::ReaderOptions::new())
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I haven't quite figured out this part
You could then return a
OwnedMessage<date::Reader>
fromfoo()
.
I put the code in that you mentioned but can't quite figure out what the body of foo is supposed to be.
pub struct OwnedMessage<T> {
phantom_data: ::std::marker::PhantomData<T>,
message: ::capnp::OwnedSpaceMessageReader,
}
impl <'a, T> OwnedMessage <T> where T: ::capnp::traits::FromPointerReader<'a> {
pub fn get(&'a self) -> ::capnp::Result<T> {
use capnp::MessageReader;
self.message.get_root()
}
}
//fn foo(mut buffer: &[u8]) -> capnp::Result<capnp::OwnedSpaceMessageReader> {
fn foo(mut buffer: &[u8]) -> OwnedMessage<date::Reader> {
// what goes here?
//capnp::serialize::read_message(&mut buffer, capnp::ReaderOptions::new())
}
in the end, i'm hoping to be able to write something like
let my_date = foo(buf);
do_something_with_a_date(&my_date);
Does this work?
pub struct OwnedMessage<T> {
phantom_data: ::std::marker::PhantomData<T>,
message: ::capnp::OwnedSpaceMessageReader,
}
impl <T> OwnedMessage <T> {
pub fn new(message: ::capnp::OwnedSpaceMessageReader) -> OwnedMessage<T> {
OwnedMessage { phantom_data: ::std::marker::PhantomData, message: message}
}
}
impl <'a, T> OwnedMessage <T> where T: ::capnp::traits::FromPointerReader<'a> {
pub fn get(&'a self) -> ::capnp::Result<T> {
use capnp::MessageReader;
self.message.get_root()
}
}
fn foo(mut buffer: &[u8]) -> ::capnp::Result<OwnedMessage<date::Reader>> {
let message = try!(::capnp::serialize::read_message(&mut buffer, ::capnp::ReaderOptions::new()));
return Ok(OwnedMessage::new(message));
}
Hm. I think there could be some lifetime issues with what I just suggested. Things will probably work out better if we forget about trying to make OwnedMessage
generic.
pub struct OwnedDateMessage {
message: ::capnp::OwnedSpaceMessageReader,
}
impl OwnedDateMessage {
pub fn get<'a>(&'a self) -> ::capnp::Result<date::Reader<'a>> {
use capnp::MessageReader;
self.message.get_root()
}
}
(I think my original suggestion with the generic OwnedMessage<T>
needs higher-kinded types to work properly.)
This is what I'm using now and it compiles/runs:
pub struct OwnedMessage<T> {
message: ::capnp::OwnedSpaceMessageReader,
phantom_data: ::std::marker::PhantomData<T>,
}
impl <'a, T> OwnedMessage <T> where T: ::capnp::traits::FromPointerReader<'a> {
pub fn new(mr: ::capnp::OwnedSpaceMessageReader) -> OwnedMessage<T> {
OwnedMessage {
message: mr,
phantom_data: ::std::marker::PhantomData
}
}
pub fn get(&'a self) -> ::capnp::Result<T> {
use capnp::MessageReader;
self.message.get_root()
}
}
fn read_indir(&self, bref: cafs_capnp::reference::block_ref::Reader) -> Result<OwnedMessage<cafs_capnp::indirect_block::Reader>> {
let indir_bytes = try!(self.read_blockref_vec(bref));
let mut cursor = io::Cursor::new(indir_bytes);
let message_reader = try!(capnp::serialize_packed::read_message(&mut cursor, capnp::message::DEFAULT_READER_OPTIONS));
Ok(OwnedMessage::new(message_reader))
}
Then I'm using it as let indir = try!(self.read_indir(bref)); let reader = try!(indir.get());
Your suggestion worked, thanks! Now I just need to figure out how to write out to a &[u8]
let x = foo(buf).unwrap();
let y = x.get().unwrap();
println!("{:?}", y.get_day()); // yayy this works
let new_date = (something about a date builder i think?)
let out_buf &[u8] = new_date.what_is_the_name_of_this_function()
Thanks for all of your help, my final program is
#[repr(C)]
pub struct bytes_output {
values : *const u8,
len : usize,
}
#[no_mangle]
pub extern fn capnp_ex(external_data: *const u8, data_len : *const size_t) -> bytes_output {
let buf : &[u8] = unsafe{ slice::from_raw_parts(external_data, data_len as usize) };
let x = foo(buf).unwrap();
let y = x.get().unwrap();
println!("capnproto year read! {:?}", y.get_year());
let mut message = MallocMessageBuilder::new_default();
{
let mut out_dt = message.init_root::<date::Builder>();
out_dt.set_year(y.get_year() + 1);
out_dt.set_month(1);
out_dt.set_day(1);
}
let mut out_buf : Vec<u8> = Vec::new();
capnp::serialize::write_message( &mut out_buf, &message );
bytes_output {
values : out_buf.as_ptr(),
len : out_buf.len(),
}
}
and the python that calls it
import cffi
import capnp
#setup ffi
ffi = cffi.FFI()
ffi.cdef('''
typedef struct {
char* values;
size_t len;
} bytes_output;
bytes_output capnp_ex(char*, size_t);
''')
lib = ffi.dlopen("./target/release/libtest.so")
# setup capnp
capnp.remove_import_hook()
example = capnp.load('schema/example.capnp')
mydate = example.Date.new_message(year=2015, month=5, day=31)
mydatebytes = mydate.to_bytes()
myresp = lib.capnp_ex(ffi.new('char[]', mydatebytes), len(mydatebytes))
def out_iter(resp):
for i in range(resp.len):
yield resp.values[i]
print(myresp)
with open('tmp.bin','wb') as outf:
for byte in out_iter(myresp):
outf.write(byte) # must be a better way to do this..
with open('tmp.bin', 'rb') as inf:
myrespdate = example.Date.read( inf )
print(myrespdate)
for some reason the python capnp implementation relies on files quite a bit and I couldn't find a way around using files.
I think this could be great for python (and other) FFI so I have been trying to figure out how to load
&[u8]
into an actual object. This is what I have so far but I think it's probably misguided.Any tips? Perhaps something to do with
read_message
? I couldn't figure it out from the examples