Skip to content

Instantly share code, notes, and snippets.

@Antti
Last active August 29, 2015 14:04
Show Gist options
  • Save Antti/b99ef9d4ca6d5d78a8ae to your computer and use it in GitHub Desktop.
Save Antti/b99ef9d4ca6d5d78a8ae to your computer and use it in GitHub Desktop.
Call rust code from ruby

#Benchmarks:

##Ruby: Command: ruby -I lib/ test_ruby.rb ~/Guitar/Nightwish\ -\ Nemo\ (Pro).gpx > /dev/null

rubinius 2.2.9 (2.1.0 8694b613 2014-06-10 JI) [x86_64-darwin13.2.0]: 18.58s user 1.12s system 129% cpu 15.184 total

ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]: 115.73s user 12.35s system 99% cpu 2:08.18 total

jruby 1.7.9 (1.9.3p392) 2013-12-06 87b108a on Java HotSpot(TM) 64-Bit Server VM 1.7.0_45-b18 [darwin-x86_64]: 79.69s user 1.41s system 223% cpu 36.251 total

##Rust: Command: ./target/gpx_reader ~/Guitar/Nightwish\ -\ Nemo\ (Pro).gpx > /dev/null

rustc 0.12.0-pre: 0.16s user 0.00s system 98% cpu 0.168 total

Ruby + Rust: Command ruby ruby_test.rb ~/Guitar/Nightwish\ -\ Nemo\ (Pro).gpx > /dev/null

Normal: 0.23s user 0.04s system 98% cpu 0.277 total

With -O flag: 0.05s user 0.00s system 97% cpu 0.052 total

Rust wrapper, so it can be called from ruby

#![crate_name = "gpx"]
#![crate_type = "dylib"]

mod gpx;
mod bitbuffer;

#[no_mangle]
pub fn parse_gpx(data: *const u8, length: uint, buffer: *mut u8, buffer_length: uint) -> int {
  let mut return_status = -1;
  unsafe {
    std::slice::raw::buf_as_slice(data, length, |dt| {
      return_status = match gpx::check_file_type(dt) {
        gpx::BCFZ => {
          let data = Vec::from_slice(dt.tailn(4));
          let content = gpx::decompress_bcfz(data);
          let len = std::cmp::min(content.len(), buffer_length);
          println!("Writing to buffer: {}, buffer size: {}, data size: {}", buffer, buffer_length, len);
          std::ptr::copy_memory(buffer, content.as_ptr(), len);
          len as int
        },
        gpx::BCFS => -2,
        gpx::Unknown => -1
      };
    });
  }
  return_status
}

Ruby wrapper to run the rust code

require 'ffi'

module RustGpx
  extend FFI::Library
  ffi_lib 'libgpx'

  attach_function :parse_gpx, :parse_gpx, [ :pointer, :uint, :pointer, :uint], :int
end


def rust_gpx(data)
  pointer = FFI::MemoryPointer.from_string(data)
  buffer_size = 20*1024*1024
  result = nil
  FFI::MemoryPointer.new(:char, buffer_size) do |buffer|
    status = RustGpx.parse_gpx(pointer, data.size, buffer, buffer.size)
    if status > 0
      result = buffer.read_string(status)
    else
      raise "Error parsing gpx #{status}"
    end
  end
  result
end

puts rust_gpx(File.read(ARGV[0]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment