Forum: Ruby passing binary data from c extension back to ruby

F62752b893edcffd0c6072d338584074?d=identicon&s=25 Kape Kape (kkaple)
on 2014-02-21 23:58
I'm writing an extension in c that will take a string of space delimited
ascii like: "param1=value1 param2=value2 ...".

The c side function will produce some binary encoded data that will vary
in length based on the input string.  The length will be known on the c
side after encoding.  What is the best way to pass this buffer of raw
data back to ruby so it knows the length and doesn't alter the data in
any way?  All ruby will need to do with it is write it to a socket then
as raw bytes knowing the length...

I see there is rb_str_new2(char*) but the data is not null terminated on
the c side so I'm hesitant to use that conversion function.

thanks,

Kape
18813f71506ebad74179bf8c5a136696?d=identicon&s=25 Eric Wong (Guest)
on 2014-02-22 00:06
(Received via mailing list)
Kape Kape <lists@ruby-forum.com> wrote:
> I see there is rb_str_new2(char*) but the data is not null terminated on
> the c side so I'm hesitant to use that conversion function.

rb_str_new(const char *ptr, long len);
3853dd5371ac1e094fc45d6c2aa0e459?d=identicon&s=25 Carlo E. Prelz (Guest)
on 2014-02-22 07:27
(Received via mailing list)
Subject: passing binary data from c extension back to ruby
  Date: ven 21 feb 14 11:58:00 +0100

Quoting Kape Kape (lists@ruby-forum.com):

> I see there is rb_str_new2(char*) but the data is not null terminated on
> the c side so I'm hesitant to use that conversion function.

As you already could read, you can use rb_str_new, and pass to it the
pointer to your data and length in bytes. This function will generate
a String object, which includes length information (no need to rely on
null-termination, thus). From Ruby, pass it to Socket#write, and all
your bytes will be transmitted, without mangling.

You can also manipulate binary data in a String from ruby using
String#unpack and Array#pack. Say that your data contains three
short unsigned integers (16bit), two signed longs (32bit) and a double
(64bit) - thus, 22 bytes. You can do

array=string.unpack('SSSlld')

and you will find in your array the six values. Array#pack does the
reverse:

string=[12,23,34,1010101,-332211,3.1415].pack('SSSlld')
p string.bytes

gives

[12, 0, 23, 0, 34, 0, 181, 105, 15, 0, 77, 238, 250, 255, 111, 18, 131,
192, 202, 33, 9, 64]

Everything works smoothly as long as your string does not use a
multibyte
encoding. The default encoding for Ruby strings is UTF-8, but strings
generated by rb_str_new and Array#pack have ASCII-8BIT encoding.

Type

ri Array#pack

to know more.

Carlo
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.