mirror of
https://github.com/fluencelabs/asmble
synced 2025-06-16 16:21:23 +00:00
Rust string example for issue #9
This commit is contained in:
2
examples/rust-string/.cargo/config
Normal file
2
examples/rust-string/.cargo/config
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
6
examples/rust-string/Cargo.toml
Normal file
6
examples/rust-string/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "rust_string"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
20
examples/rust-string/README.md
Normal file
20
examples/rust-string/README.md
Normal 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.
|
46
examples/rust-string/src/lib.rs
Normal file
46
examples/rust-string/src/lib.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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!"));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user