I am trying to use the ruby C-api to call unpack
on a ruby string.
Here is my current code:
#include "ruby.h"#include <stdio.h>#include <unistd.h>VALUE dangerous_func(unsigned char* buf) { VALUE hello_world_str; VALUE format_string; hello_world_str = rb_str_new_cstr(buf); // Create ruby string from buffer. format_string = rb_str_new_cstr("b8B8"); // Example format string. printf("Before...\n"); rb_funcall(hello_world_str, rb_intern("unpack"), 1, format_string); // Pass the format string as argument. printf("After...\n"); return Qnil; // Just return Qnil}// This is needed for rb_rescueVALUE error_func(VALUE stuff) { return Qnil;}#define MAX_INPUT_SIZE 1000int main(int argc, char **argv) { unsigned char input[MAX_INPUT_SIZE+1]; // Input buffer int len; memset(input, 0x00, MAX_INPUT_SIZE+1); // Zero out input buffer ruby_setup(); ruby_sysinit(&argc, &argv); RUBY_INIT_STACK; ruby_init(); ruby_init_loadpath(); len = read(0, input, MAX_INPUT_SIZE); rb_rescue(dangerous_func, input, error_func, Qnil); // Call the function ruby_finalize(); // Cleanup return 0;}
After testing the code with the input aa
, it just prints Before...
and exits, therefore it exits in the rb_funcall function.
I looked around the ruby codebase and in the string.c
file, there are many method definitions for the string object, but none of which was unpack
or pack
:
// ... rb_define_method(rb_cString, "sub", rb_str_sub, -1); rb_define_method(rb_cString, "gsub", rb_str_gsub, -1); rb_define_method(rb_cString, "chop", rb_str_chop, 0); rb_define_method(rb_cString, "chomp", rb_str_chomp, -1); rb_define_method(rb_cString, "strip", rb_str_strip, 0); rb_define_method(rb_cString, "lstrip", rb_str_lstrip, 0); rb_define_method(rb_cString, "rstrip", rb_str_rstrip, 0); rb_define_method(rb_cString, "delete_prefix", rb_str_delete_prefix, 1); rb_define_method(rb_cString, "delete_suffix", rb_str_delete_suffix, 1); rb_define_method(rb_cString, "sub!", rb_str_sub_bang, -1); rb_define_method(rb_cString, "gsub!", rb_str_gsub_bang, -1); rb_define_method(rb_cString, "chop!", rb_str_chop_bang, 0); rb_define_method(rb_cString, "chomp!", rb_str_chomp_bang, -1); rb_define_method(rb_cString, "strip!", rb_str_strip_bang, 0); rb_define_method(rb_cString, "lstrip!", rb_str_lstrip_bang, 0); rb_define_method(rb_cString, "rstrip!", rb_str_rstrip_bang, 0); rb_define_method(rb_cString, "delete_prefix!", rb_str_delete_prefix_bang, 1); rb_define_method(rb_cString, "delete_suffix!", rb_str_delete_suffix_bang, 1); rb_define_method(rb_cString, "tr", rb_str_tr, 2); rb_define_method(rb_cString, "tr_s", rb_str_tr_s, 2); rb_define_method(rb_cString, "delete", rb_str_delete, -1); rb_define_method(rb_cString, "squeeze", rb_str_squeeze, -1); rb_define_method(rb_cString, "count", rb_str_count, -1); rb_define_method(rb_cString, "tr!", rb_str_tr_bang, 2); rb_define_method(rb_cString, "tr_s!", rb_str_tr_s_bang, 2); rb_define_method(rb_cString, "delete!", rb_str_delete_bang, -1); rb_define_method(rb_cString, "squeeze!", rb_str_squeeze_bang, -1); rb_define_method(rb_cString, "each_line", rb_str_each_line, -1); rb_define_method(rb_cString, "each_byte", rb_str_each_byte, 0); rb_define_method(rb_cString, "each_char", rb_str_each_char, 0); rb_define_method(rb_cString, "each_codepoint", rb_str_each_codepoint, 0); rb_define_method(rb_cString, "each_grapheme_cluster", rb_str_each_grapheme_cluster, 0); rb_define_method(rb_cString, "sum", rb_str_sum, -1); rb_define_method(rb_cString, "slice", rb_str_aref_m, -1); rb_define_method(rb_cString, "slice!", rb_str_slice_bang, -1); rb_define_method(rb_cString, "partition", rb_str_partition, 1); rb_define_method(rb_cString, "rpartition", rb_str_rpartition, 1); rb_define_method(rb_cString, "encoding", rb_obj_encoding, 0); /* in encoding.c */ rb_define_method(rb_cString, "force_encoding", rb_str_force_encoding, 1); rb_define_method(rb_cString, "b", rb_str_b, 0); rb_define_method(rb_cString, "valid_encoding?", rb_str_valid_encoding_p, 0); rb_define_method(rb_cString, "ascii_only?", rb_str_is_ascii_only_p, 0); /* define UnicodeNormalize module here so that we don't have to look it up */ mUnicodeNormalize = rb_define_module("UnicodeNormalize"); id_normalize = rb_intern_const("normalize"); id_normalized_p = rb_intern_const("normalized?"); rb_define_method(rb_cString, "unicode_normalize", rb_str_unicode_normalize, -1); rb_define_method(rb_cString, "unicode_normalize!", rb_str_unicode_normalize_bang, -1); rb_define_method(rb_cString, "unicode_normalized?", rb_str_unicode_normalized_p, -1); rb_fs = Qnil; rb_define_hooked_variable("$;", &rb_fs, 0, rb_fs_setter); rb_define_hooked_variable("$-F", &rb_fs, 0, rb_fs_setter); rb_gc_register_address(&rb_fs); rb_cSymbol = rb_define_class("Symbol", rb_cObject); rb_include_module(rb_cSymbol, rb_mComparable); rb_undef_alloc_func(rb_cSymbol); rb_undef_method(CLASS_OF(rb_cSymbol), "new"); rb_define_singleton_method(rb_cSymbol, "all_symbols", sym_all_symbols, 0); rb_define_method(rb_cSymbol, "==", sym_equal, 1); rb_define_method(rb_cSymbol, "===", sym_equal, 1); rb_define_method(rb_cSymbol, "inspect", sym_inspect, 0); rb_define_method(rb_cSymbol, "name", rb_sym2str, 0); /* in symbol.c */ rb_define_method(rb_cSymbol, "to_proc", rb_sym_to_proc, 0); /* in proc.c */ rb_define_method(rb_cSymbol, "succ", sym_succ, 0); rb_define_method(rb_cSymbol, "next", sym_succ, 0); rb_define_method(rb_cSymbol, "<=>", sym_cmp, 1); rb_define_method(rb_cSymbol, "casecmp", sym_casecmp, 1); rb_define_method(rb_cSymbol, "casecmp?", sym_casecmp_p, 1); rb_define_method(rb_cSymbol, "=~", sym_match, 1); rb_define_method(rb_cSymbol, "[]", sym_aref, -1); rb_define_method(rb_cSymbol, "slice", sym_aref, -1); rb_define_method(rb_cSymbol, "length", sym_length, 0);// ...
there is also the pack.rb
file:
class Array # call-seq: # pack(template, buffer: nil) -> string # # Formats each element in +self+ into a binary string; returns that string. # See {Packed Data}[rdoc-ref:packed_data.rdoc]. def pack(fmt, buffer: nil) Primitive.pack_pack(fmt, buffer) endendclass String # call-seq: # unpack(template, offset: 0) -> array # # Extracts data from +self+, forming objects that become the elements of a new array; # returns that array. # See {Packed Data}[rdoc-ref:packed_data.rdoc]. def unpack(fmt, offset: 0) Primitive.pack_unpack(fmt, offset) end # call-seq: # unpack1(template, offset: 0) -> object # # Like String#unpack, but unpacks and returns only the first extracted object. # See {Packed Data}[rdoc-ref:packed_data.rdoc]. def unpack1(fmt, offset: 0) Primitive.pack_unpack1(fmt, offset) endend
and it seems that we should call pack_unpack
instead, but neither pack_unpack
exists in the string method definitions. I tried to replace the unpack
method with other methods and it works as intended, but when I try to use unpack
, it exits before execution can get to the After...
line.
So, my question is, how do I call the unpack
method on a ruby string in ruby C-api?
I want the C code to be basically the same as this:
puts "Before..."(ARGF.read).unpack("b8B8")puts "After..."
but just in C.