/rust/registry/src/index.crates.io-1949cf8c6b5b557f/diplomat-runtime-0.8.2/src/writeable.rs
Line | Count | Source |
1 | | use alloc::boxed::Box; |
2 | | use alloc::vec::Vec; |
3 | | use core::ffi::c_void; |
4 | | use core::{fmt, ptr}; |
5 | | |
6 | | /// An object that can one can write UTF-8 strings to |
7 | | /// |
8 | | /// This allows the C API to write to arbitrary kinds of objects, for example a |
9 | | /// C++ std::string or a char buffer. |
10 | | /// |
11 | | /// The way to use this object is to fill out the `buf`, `len`, `cap` fields with |
12 | | /// appropriate values for the buffer, its current length, and its current capacity, |
13 | | /// and `flush` and `grow` with appropriate callbacks (using `context` to reference any |
14 | | /// state they need). This object will be passed by mutable reference to the Rust side, |
15 | | /// and Rust will write to it, calling `grow()` as necessary. Once done, it will call `flush()` |
16 | | /// to update any state on `context` (e.g. adding a null terminator, updating the length). |
17 | | /// The object on the foreign side will be directly usable after this, the foreign side |
18 | | /// need not perform additional state updates after passing an [`DiplomatWriteable`] to |
19 | | /// a function. |
20 | | /// |
21 | | /// [`diplomat_simple_writeable()`] can be used to write to a fixed-size char buffer. |
22 | | /// |
23 | | /// May be extended in the future to support further invariants |
24 | | /// |
25 | | /// DiplomatWriteable will not perform any cleanup on `context` or `buf`, these are logically |
26 | | /// "borrows" from the FFI side. |
27 | | /// |
28 | | /// # Safety invariants: |
29 | | /// - `flush()` and `grow()` will be passed `self` including `context` and it should always be safe to do so. |
30 | | /// `context` may be null, however `flush()` and `grow()` must then be ready to receive it as such. |
31 | | /// - `buf` must be `cap` bytes long |
32 | | /// - `grow()` must either return false or update `buf` and `cap` for a valid buffer |
33 | | /// of at least the requested buffer size |
34 | | /// - `DiplomatWriteable::flush()` will be automatically called by Diplomat. `flush()` might also be called |
35 | | /// (erroneously) on the Rust side (it's a public method), so it must be idempotent. |
36 | | #[repr(C)] |
37 | | pub struct DiplomatWriteable { |
38 | | /// Context pointer for additional data needed by `grow()` and `flush()`. May be `null`. |
39 | | /// |
40 | | /// The pointer may reference structured data on the foreign side, |
41 | | /// such as C++ std::string, used to reallocate buf. |
42 | | context: *mut c_void, |
43 | | /// The raw string buffer, which will be mutated on the Rust side. |
44 | | buf: *mut u8, |
45 | | /// The current filled size of the buffer |
46 | | len: usize, |
47 | | /// The current capacity of the buffer |
48 | | cap: usize, |
49 | | /// Called by Rust to indicate that there is no more data to write. |
50 | | /// |
51 | | /// May be called multiple times. |
52 | | /// |
53 | | /// Arguments: |
54 | | /// - `self` (`*mut DiplomatWriteable`): This `DiplomatWriteable` |
55 | | flush: extern "C" fn(*mut DiplomatWriteable), |
56 | | /// Called by Rust to request more capacity in the buffer. The implementation should allocate a new |
57 | | /// buffer and copy the contents of the old buffer into the new buffer, updating `self.buf` and `self.cap` |
58 | | /// |
59 | | /// Arguments: |
60 | | /// - `self` (`*mut DiplomatWriteable`): This `DiplomatWriteable` |
61 | | /// - `capacity` (`usize`): The requested capacity. |
62 | | /// |
63 | | /// Returns: `true` if the allocation succeeded. Should not update any state if it failed. |
64 | | grow: extern "C" fn(*mut DiplomatWriteable, usize) -> bool, |
65 | | } |
66 | | |
67 | | impl DiplomatWriteable { |
68 | | /// Call this function before releasing the buffer to C |
69 | 0 | pub fn flush(&mut self) { |
70 | 0 | (self.flush)(self); |
71 | 0 | } |
72 | | } |
73 | | impl fmt::Write for DiplomatWriteable { |
74 | 0 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { |
75 | 0 | let needed_len = self.len + s.len(); |
76 | 0 | if needed_len > self.cap { |
77 | 0 | let success = (self.grow)(self, needed_len); |
78 | 0 | if !success { |
79 | 0 | return Err(fmt::Error); |
80 | 0 | } |
81 | 0 | } |
82 | 0 | debug_assert!(needed_len <= self.cap); |
83 | 0 | unsafe { |
84 | 0 | ptr::copy_nonoverlapping(s.as_bytes().as_ptr(), self.buf.add(self.len), s.len()); |
85 | 0 | } |
86 | 0 | self.len = needed_len; |
87 | 0 | Ok(()) |
88 | 0 | } |
89 | | } |
90 | | |
91 | | /// Create an `DiplomatWriteable` that can write to a fixed-length stack allocated `u8` buffer. |
92 | | /// |
93 | | /// Once done, this will append a null terminator to the written string. |
94 | | /// |
95 | | /// # Safety |
96 | | /// |
97 | | /// - `buf` must be a valid pointer to a region of memory that can hold at `buf_size` bytes |
98 | | #[no_mangle] |
99 | 0 | pub unsafe extern "C" fn diplomat_simple_writeable( |
100 | 0 | buf: *mut u8, |
101 | 0 | buf_size: usize, |
102 | 0 | ) -> DiplomatWriteable { |
103 | 0 | extern "C" fn grow(_this: *mut DiplomatWriteable, _cap: usize) -> bool { |
104 | 0 | false |
105 | 0 | } |
106 | 0 | extern "C" fn flush(this: *mut DiplomatWriteable) { |
107 | | unsafe { |
108 | 0 | debug_assert!((*this).len <= (*this).cap); |
109 | 0 | let buf = (*this).buf; |
110 | 0 | ptr::write(buf.add((*this).len), 0) |
111 | | } |
112 | 0 | } |
113 | 0 | DiplomatWriteable { |
114 | 0 | context: ptr::null_mut(), |
115 | 0 | buf, |
116 | 0 | len: 0, |
117 | 0 | // keep an extra byte in our pocket for the null terminator |
118 | 0 | cap: buf_size - 1, |
119 | 0 | flush, |
120 | 0 | grow, |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | /// Create an [`DiplomatWriteable`] that can write to a dynamically allocated buffer managed by Rust. |
125 | | /// |
126 | | /// Use [`diplomat_buffer_writeable_destroy()`] to free the writable and its underlying buffer. |
127 | | #[no_mangle] |
128 | 0 | pub extern "C" fn diplomat_buffer_writeable_create(cap: usize) -> *mut DiplomatWriteable { |
129 | 0 | extern "C" fn grow(this: *mut DiplomatWriteable, new_cap: usize) -> bool { |
130 | 0 | unsafe { |
131 | 0 | let this = this.as_mut().unwrap(); |
132 | 0 | let mut vec = Vec::from_raw_parts(this.buf, 0, this.cap); |
133 | 0 | vec.reserve(new_cap); |
134 | 0 | this.cap = vec.capacity(); |
135 | 0 | this.buf = vec.as_mut_ptr(); |
136 | 0 | core::mem::forget(vec); |
137 | 0 | } |
138 | 0 | true |
139 | 0 | } |
140 | | |
141 | 0 | extern "C" fn flush(_: *mut DiplomatWriteable) {} |
142 | | |
143 | 0 | let mut vec = Vec::<u8>::with_capacity(cap); |
144 | 0 | let ret = DiplomatWriteable { |
145 | 0 | context: ptr::null_mut(), |
146 | 0 | buf: vec.as_mut_ptr(), |
147 | 0 | len: 0, |
148 | 0 | cap, |
149 | 0 | flush, |
150 | 0 | grow, |
151 | 0 | }; |
152 | | |
153 | 0 | core::mem::forget(vec); |
154 | 0 | Box::into_raw(Box::new(ret)) |
155 | 0 | } |
156 | | |
157 | | /// Grabs a pointer to the underlying buffer of a writable. |
158 | | /// |
159 | | /// # Safety |
160 | | /// - The returned pointer is valid until the passed writable is destroyed. |
161 | | /// - `this` must be a pointer to a valid [`DiplomatWriteable`] constructed by |
162 | | /// [`diplomat_buffer_writeable_create()`]. |
163 | | #[no_mangle] |
164 | 0 | pub extern "C" fn diplomat_buffer_writeable_get_bytes(this: &DiplomatWriteable) -> *mut u8 { |
165 | 0 | this.buf |
166 | 0 | } |
167 | | |
168 | | /// Gets the length in bytes of the content written to the writable. |
169 | | /// |
170 | | /// # Safety |
171 | | /// - `this` must be a pointer to a valid [`DiplomatWriteable`] constructed by |
172 | | /// [`diplomat_buffer_writeable_create()`]. |
173 | | #[no_mangle] |
174 | 0 | pub extern "C" fn diplomat_buffer_writeable_len(this: &DiplomatWriteable) -> usize { |
175 | 0 | this.len |
176 | 0 | } |
177 | | |
178 | | /// Destructor for Rust-memory backed writables. |
179 | | /// |
180 | | /// # Safety |
181 | | /// - `this` must be a pointer to a valid [`DiplomatWriteable`] constructed by |
182 | | /// [`diplomat_buffer_writeable_create()`]. |
183 | | #[no_mangle] |
184 | 0 | pub unsafe extern "C" fn diplomat_buffer_writeable_destroy(this: *mut DiplomatWriteable) { |
185 | 0 | let this = Box::from_raw(this); |
186 | 0 | let vec = Vec::from_raw_parts(this.buf, 0, this.cap); |
187 | 0 | drop(vec); |
188 | 0 | drop(this); |
189 | 0 | } |