Rust string example for issue #9

This commit is contained in:
Chad Retz
2017-11-29 13:03:36 -06:00
parent e51da3116e
commit 8b51e14c33
11 changed files with 221 additions and 4 deletions

View File

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View File

@ -0,0 +1,6 @@
[package]
name = "rust_string"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]

View File

@ -0,0 +1,20 @@
### Example: Rust String
This shows an example of using Rust strings on the JVM. This builds on [rust-simple](../rust-simple).
In this version, we do allocation and deallocation on the Rust side and we have a Java pointer object that deallocates
on finalization. Inside of Rust, we make sure not to take ownership of any of the data. To demonstrate string use, we
implement two functions on the Rust side: one for string length and another for prepending "From Rust: ". Both don't
take strings directly, but instead pointers and lengths to the byte arrays.
To run it yourself, run the following from the root `asmble` dir:
gradlew --no-daemon :examples:rust-string:run
In release mode, the generated class is 128KB w/ a bit over 200 methods, but it is quite fast. The output:
Char count of 'tester': 6
Char count of Russian hello (Здравствуйте): 12
From Rust: Hello, World!
For me on Windows, the Russian word above just appears as `????????????`, but the rest is right.

View File

@ -0,0 +1,46 @@
#![feature(allocator_api)]
use std::heap::{Alloc, Heap, Layout};
use std::ffi::{CString};
use std::mem;
use std::os::raw::c_char;
use std::str;
#[no_mangle]
pub extern "C" fn string_len(ptr: *mut u8, len: usize) -> usize {
unsafe {
let bytes = Vec::<u8>::from_raw_parts(ptr, len, len);
let len = str::from_utf8(&bytes).unwrap().chars().count();
mem::forget(bytes);
len
}
}
#[no_mangle]
pub extern "C" fn prepend_from_rust(ptr: *mut u8, len: usize) -> *const c_char {
unsafe {
let bytes = Vec::<u8>::from_raw_parts(ptr, len, len);
let s = str::from_utf8(&bytes).unwrap();
mem::forget(s);
let cstr = CString::new(format!("From Rust: {}", s)).unwrap();
let ret = cstr.as_ptr();
mem::forget(cstr);
return ret
}
}
#[no_mangle]
pub extern "C" fn alloc(size: usize) -> *mut u8 {
unsafe {
let layout = Layout::from_size_align(size, mem::align_of::<u8>()).unwrap();
Heap.alloc(layout).unwrap()
}
}
#[no_mangle]
pub extern "C" fn dealloc(ptr: *mut u8, size: usize) {
unsafe {
let layout = Layout::from_size_align(size, mem::align_of::<u8>()).unwrap();
Heap.dealloc(ptr, layout);
}
}

View File

@ -0,0 +1,93 @@
package asmble.examples.ruststring;
import asmble.generated.RustString;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
// Not thread safe!
class Lib {
private final RustString rustString;
Lib(RustString rustString) {
this.rustString = rustString;
}
int stringLength(String str) {
Ptr strPtr = ptrFromString(str);
return rustString.string_len(strPtr.offset, strPtr.size);
}
String prependFromRust(String str) {
Ptr strPtr = ptrFromString(str);
int nullTermOffset = rustString.prepend_from_rust(strPtr.offset, strPtr.size);
return nullTermedStringFromOffset(nullTermOffset);
}
private Ptr ptrFromString(String str) {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
Ptr ptr = new Ptr(bytes.length);
ptr.put(bytes);
return ptr;
}
private String nullTermedStringFromOffset(int offset) {
ByteBuffer memory = rustString.getMemory();
memory.position(offset);
// We're going to turn the mem into an input stream. This is the
// reasonable way to stream a UTF8 read using standard Java libs
// that I could find.
InputStreamReader r = new InputStreamReader(new InputStream() {
@Override
public int read() throws IOException {
if (!memory.hasRemaining()) {
return -1;
}
return memory.get() & 0xFF;
}
}, StandardCharsets.UTF_8);
StringBuilder builder = new StringBuilder();
try {
while (true) {
int c = r.read();
if (c <= 0) {
break;
}
builder.append((char) c);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
rustString.dealloc(offset, memory.position() - offset);
return builder.toString();
}
class Ptr {
final int offset;
final int size;
Ptr(int offset, int size) {
this.offset = offset;
this.size = size;
}
Ptr(int size) {
this(rustString.alloc(size), size);
}
void put(byte[] bytes) {
// Yeah, yeah, not thread safe
ByteBuffer memory = rustString.getMemory();
memory.position(offset);
memory.put(bytes);
}
@Override
protected void finalize() throws Throwable {
rustString.dealloc(offset, size);
}
}
}

View File

@ -0,0 +1,17 @@
package asmble.examples.ruststring;
import asmble.generated.RustString;
public class Main {
// 20 pages is good for now
private static final int PAGE_SIZE = 65536;
private static final int MAX_MEMORY = 20 * PAGE_SIZE;
public static void main(String[] args) {
Lib lib = new Lib(new RustString(MAX_MEMORY));
System.out.println("Char count of 'tester': " + lib.stringLength("tester"));
String russianHello = "\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435";
System.out.println("Char count of Russian hello (" + russianHello + "): " + lib.stringLength(russianHello));
System.out.println(lib.prependFromRust("Hello, World!"));
}
}