-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FFI for FixedDecimalFormat #680
Changes from 3 commits
efb424a
a4be6b2
e90813b
e8b6793
3de282e
146db26
14a2758
9637209
fbdea4d
bbea30e
8aade18
bd79661
8f200eb
63b00c7
5b56dc0
f2fa2a8
c1536bb
c5ac2c5
275497c
fa7967c
e933349
7247566
054ad82
ec9e250
425ec0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ | |
#include <stdbool.h> | ||
|
||
typedef struct { | ||
void* data; | ||
void* context; | ||
char* buf; | ||
size_t len; | ||
size_t cap; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,23 +9,31 @@ use std::{fmt, ptr}; | |
/// | ||
/// [`icu4x_simple_writeable()`] can be used to write to a fixed-size char buffer. | ||
/// | ||
/// May be extended in the future to support further invariants | ||
/// | ||
/// ICU4XCustomWriteable will not perform any cleanup on `context` or `buf`, these are logically | ||
/// "borrows" from the FFI side. | ||
/// | ||
/// # Safety invariants: | ||
/// - `flush()` and `grow()` will be passed `data` and the value should be valid for that | ||
/// `data` may be null, however `flush()` and `grow()` must then be ready to receive it | ||
/// - `flush()` and `grow()` will be passed `context` and the value should be valid for that | ||
/// `context` may be null, however `flush()` and `grow()` must then be ready to receive it | ||
/// - `buf` must be `cap` bytes long | ||
/// - `grow()` must either return null or a valid buffer of at least the requested buffer size | ||
/// - Rust code must call `ICU4XCustomWriteable::flush()` before releasing to C | ||
pub struct ICU4XCustomWriteable { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Let's bikeshed the name of this struct. Why did you put "Custom" in the name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because I copied the name from the doc, I'm not particularly attached to it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. works for me! |
||
/// Pointer to the actual object. While we're writing, we will write | ||
/// directly to `buf` without updating `data`'s state. | ||
data: *mut c_void, | ||
/// The buffer to write to | ||
/// directly to `buf` without updating `context`'s state, this pointer exists so that | ||
/// `grow()` and `flush()` can get access to the full object on the foreign side. | ||
/// | ||
/// This can be null if the | ||
context: *mut c_void, | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The buffer to write directly to | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
buf: *mut u8, | ||
/// The current filled size of the buffer | ||
len: usize, | ||
/// The current capacity of the buffer | ||
cap: usize, | ||
/// Called by Rust code when it is done writing, updating `data` | ||
/// Called by Rust code when it is done writing, updating `context` | ||
/// with the new length | ||
flush: extern "C" fn(*mut c_void, usize), | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// Called by Rust when it needs more capacity, passing | ||
|
@@ -41,21 +49,21 @@ pub struct ICU4XCustomWriteable { | |
impl ICU4XCustomWriteable { | ||
/// Call this function before releasing the buffer to C | ||
pub fn flush(&mut self) { | ||
(self.flush)(self.data, self.len); | ||
(self.flush)(self.context, self.len); | ||
} | ||
} | ||
impl fmt::Write for ICU4XCustomWriteable { | ||
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { | ||
let mut needed_len = self.len + s.len(); | ||
let needed_len = self.len + s.len(); | ||
if needed_len > self.cap { | ||
let newbuf = (self.grow)(self.data, &mut needed_len); | ||
let mut new_cap = needed_len; | ||
let newbuf = (self.grow)(self.context, &mut new_cap); | ||
if newbuf.is_null() { | ||
return Err(fmt::Error); | ||
} | ||
self.cap = needed_len; | ||
self.cap = new_cap; | ||
self.buf = newbuf; | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
let needed_len = self.len + s.len(); | ||
debug_assert!(needed_len <= self.cap); | ||
unsafe { | ||
ptr::copy_nonoverlapping( | ||
|
@@ -74,19 +82,20 @@ impl fmt::Write for ICU4XCustomWriteable { | |
/// Once done, this will append a null terminator to the written string. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn icu4x_simple_writeable(buf: *mut u8, len: usize) -> ICU4XCustomWriteable { | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
extern "C" fn grow(_data: *mut c_void, _cap: *mut usize) -> *mut u8 { | ||
extern "C" fn grow(_context: *mut c_void, _cap: *mut usize) -> *mut u8 { | ||
ptr::null_mut() | ||
} | ||
extern "C" fn flush(data: *mut c_void, size: usize) { | ||
extern "C" fn flush(context: *mut c_void, size: usize) { | ||
unsafe { | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let buf = data as *mut u8; | ||
let buf = context as *mut u8; | ||
ptr::write(buf.offset(size as isize), 0) | ||
Manishearth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
ICU4XCustomWriteable { | ||
data: buf as *mut c_void, | ||
context: buf as *mut c_void, | ||
buf, | ||
len: 0, | ||
// keep an extra byte in our pocket for the null terminator | ||
cap: len - 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: To me, this feels more like example or test code, rather than something that is a core part of CustomWriteable. I'd prefer to see an implementation in C that lives as part of the example code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a common enough use case that it's good to ship a basic CustomWriteable so that it's possible to experiment with ICU4X without too much boilerplate. |
||
flush, | ||
grow, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This comment is not relevant for safety of the struct, because:
context
is only read on the C side.unsafe
to reference a pointer.I would delete this line and explain it further in the docs for
context
below.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's still important to document what
flush
andgrow
expect.