pub enum FzString<'a> {
Null,
String(String),
CString(CString),
CStr(&'a CStr),
Bytes(Vec<u8>),
}
Expand description
A FzString carries a single string between Rust and C code, represented from the C side as an opaque struct.
The two environments carry some different requirements: C generally requires that strings be NUL-terminated, while Rust requires that strings be valid UTF-8. Rust also permits NUL characters in the middle of a string.
This type accepts whatever kind of data it receives without error, and converts – potentially with an error – when output of a different kind is required.
A number of From<T>
implementations are provided to convert from common Rust types. The
fz_string_..
utility functions provide conversions from various string formats.
FzStrings also have a special “Null” state, similar to the None variant of Option. For user
convenience, a NULL pointer is treated as a pointer to the Null variant wherever a pointer is
accepted. Rust code should use the _nonnull
methods where the Null variant is not allowed.
Note that the Null variant is not necessarily represented with an all-zero byte pattern.
A FzString points to allocated memory, and must be freed to avoid memory leaks.
Variants§
Null
An un-set FzString.
String(String)
An owned Rust string (not NUL-terminated, valid UTF-8).
CString(CString)
An owned C String (NUL-terminated, may contain invalid UTF-8).
CStr(&'a CStr)
A borrowed C string.
Bytes(Vec<u8>)
An owned bunch of bytes (not NUL-terminated, may contain invalid UTF-8).
Implementations§
Source§impl<'a> FzString<'a>
impl<'a> FzString<'a>
Sourcepub fn as_str(&mut self) -> Result<Option<&str>, InvalidUTF8Error>
pub fn as_str(&mut self) -> Result<Option<&str>, InvalidUTF8Error>
Convert this value to &str
.
If required, the FzString is converted in-place to a String variant. If this conversion fails because the content is not valid UTF-8, an error is returned.
The Null FzString is represented as None.
Examples found in repository?
114pub unsafe extern "C" fn kvstore_get(
115 store: *mut kvstore_t,
116 key: *mut kvstore_string_t,
117) -> kvstore_string_t {
118 // SAFETY:
119 // - store is not NULL and valid (see docstring)
120 // - store is valid for the life of this function (documented as not threadsafe)
121 // - store will not be accessed during the life of this function (documented as not threadsafe)
122 unsafe {
123 BoxedStore::with_ref_nonnull(store, |store| {
124 // SAFETY:
125 // - key must be a valid kvstore_string_t (docstring)
126 // - key will not be accessed concurrency (type docstring)
127 match unsafe {
128 FzString::with_ref_mut(key, |key| {
129 if let Ok(Some(key)) = key.as_str() {
130 store.get(key)
131 } else {
132 None // Null key or invalid UTF-8 looks the same as key-not-found
133 }
134 })
135 } {
136 // SAFETY:
137 // - the caller will free the returned value (see docstring)
138 Some(val) => unsafe { FzString::return_val(FzString::String(val.to_string())) },
139 // SAFETY:
140 // - the caller will free the returned value (see docstring)
141 None => unsafe { FzString::return_val(FzString::Null) },
142 }
143 })
144 }
145}
146
147#[ffizz_header::item]
148#[ffizz(order = 30)]
149/// Set a value in the kvstore_t, consuming the key and value. Returns false on error.
150///
151/// # Safety
152///
153/// The store must be non-NULL and point to a valid kvstore_t.
154///
155/// The key and value must both be valid kvstore_string_t values, must not be otherwise accessed
156/// while this function executes, and are invalid when this function returns.
157///
158/// # Note
159///
160/// The kvstore API sometimes invalidates its string arguments and sometimes leaves that
161/// reponsibility to the caller, which could lead to confusion for users of the library. It's done
162/// here for example purposes only!
163///
164/// ```c
165/// bool kvstore_set(kvstore *store, kvstore_string_t *key, kvstore_string_t *value);
166/// ```
167#[no_mangle]
168pub unsafe extern "C" fn kvstore_set(
169 store: *mut kvstore_t,
170 key: *mut kvstore_string_t,
171 val: *mut kvstore_string_t,
172) -> bool {
173 // SAFETY:
174 // - store is not NULL and valid (see docstring)
175 // - store is valid for the life of this function (documented as not threadsafe)
176 // - store will not be accessed during the life of this function (documented as not threadsafe)
177 unsafe {
178 BoxedStore::with_ref_mut_nonnull(store, |store| {
179 // SAFETY:
180 // - key/val are valid kvstore_string_t's (see docstring)
181 // - key/val are not accessed concurrently (type docstring)
182 // - key/val are not uesd after function returns (see docstring)
183 let (key, val) = unsafe { (FzString::take_ptr(key), FzString::take_ptr(val)) };
184
185 if let Ok(Some(key)) = key.into_string() {
186 if let Ok(Some(val)) = val.into_string() {
187 store.set(key, val);
188 return true;
189 }
190 }
191 false
192 })
193 }
194}
195
196#[ffizz_header::item]
197#[ffizz(order = 30)]
198/// Delete a value from the kvstore_t. Returns false on error.
199///
200/// # Safety
201///
202/// The store must be non-NULL and point to a valid kvstore_t.
203///
204/// The key must be a valid kvstore_string_t, must not be otherwise accessed while this function
205/// executes, and will remain valid after this function returns.
206/// ```c
207/// bool kvstore_del(kvstore *store, kvstore_string_t *key);
208/// ```
209#[no_mangle]
210pub unsafe extern "C" fn kvstore_del(store: *mut kvstore_t, key: *mut kvstore_string_t) -> bool {
211 // SAFETY:
212 // - key must be a valid kvstore_string_t (docstring)
213 // - key will not be accessed concurrency (type docstring)
214 unsafe {
215 FzString::with_ref_mut(key, move |key| {
216 // SAFETY:
217 // - store is not NULL and valid (see docstring)
218 // - store is valid for the life of this function (documented as not threadsafe)
219 // - store will not be accessed during the life of this function (documented as not threadsafe)
220 unsafe {
221 BoxedStore::with_ref_mut_nonnull(store, move |store| {
222 if let Ok(Some(key)) = key.as_str() {
223 store.del(key);
224 true
225 } else {
226 false
227 }
228 })
229 }
230 })
231 }
232}
Sourcepub fn as_str_nonnull(&mut self) -> Result<&str, InvalidUTF8Error>
pub fn as_str_nonnull(&mut self) -> Result<&str, InvalidUTF8Error>
Convert this FzString, assuming it is not Null, into &str
.
This is a simple wrapper that will panic on the Null variant. This is useful when the C API prohibits NULL.
Sourcepub fn as_cstr(&mut self) -> Result<Option<&CStr>, EmbeddedNulError>
pub fn as_cstr(&mut self) -> Result<Option<&CStr>, EmbeddedNulError>
Convert this value to a CStr: a slice of bytes containing a valid, NUL-terminated C string.
If required, the FzString is converted in-place to a CString variant. If this conversion fails because the content contains embedded NUL characters, an error is returned.
The Null FzString is represented as None.
Sourcepub fn as_cstr_nonnull(&mut self) -> Result<&CStr, EmbeddedNulError>
pub fn as_cstr_nonnull(&mut self) -> Result<&CStr, EmbeddedNulError>
Convert this FzString, assuming it is not Null, into a CStr.
This is a simple wrapper that will panic on the Null variant. This is useful when the C API prohibits NULL.
Sourcepub fn into_string(self) -> Result<Option<String>, InvalidUTF8Error>
pub fn into_string(self) -> Result<Option<String>, InvalidUTF8Error>
Consume this FzString and return an equivalent String.
As with as_str
, the FzString is converted in-place, and this conversion can fail. In the
failure case, the original data is lost.
The Null varaiant is represented as None.
Examples found in repository?
168pub unsafe extern "C" fn kvstore_set(
169 store: *mut kvstore_t,
170 key: *mut kvstore_string_t,
171 val: *mut kvstore_string_t,
172) -> bool {
173 // SAFETY:
174 // - store is not NULL and valid (see docstring)
175 // - store is valid for the life of this function (documented as not threadsafe)
176 // - store will not be accessed during the life of this function (documented as not threadsafe)
177 unsafe {
178 BoxedStore::with_ref_mut_nonnull(store, |store| {
179 // SAFETY:
180 // - key/val are valid kvstore_string_t's (see docstring)
181 // - key/val are not accessed concurrently (type docstring)
182 // - key/val are not uesd after function returns (see docstring)
183 let (key, val) = unsafe { (FzString::take_ptr(key), FzString::take_ptr(val)) };
184
185 if let Ok(Some(key)) = key.into_string() {
186 if let Ok(Some(val)) = val.into_string() {
187 store.set(key, val);
188 return true;
189 }
190 }
191 false
192 })
193 }
194}
Sourcepub fn into_string_nonnull(self) -> Result<String, InvalidUTF8Error>
pub fn into_string_nonnull(self) -> Result<String, InvalidUTF8Error>
Consume this FzString, assuming it is not Null, and return an equivalent String.
This is a simple wrapper that will panic on the Null variant. This is useful when the C API prohibits NULL.
Sourcepub fn into_path_buf(self) -> Result<Option<PathBuf>, Utf8Error>
pub fn into_path_buf(self) -> Result<Option<PathBuf>, Utf8Error>
Consume this FzString and return an equivalent PathBuf.
As with as_str
, the FzString is converted in-place, and this conversion can fail. In the
failure case, the original data is lost.
The Null varaiant is represented as None.
Sourcepub fn into_path_buf_nonnull(self) -> Result<PathBuf, Utf8Error>
pub fn into_path_buf_nonnull(self) -> Result<PathBuf, Utf8Error>
Consume this FzString, assuming it is not Null, and return an equivalent PathBuf.
This is a simple wrapper that will panic on the Null variant. This is useful when the C API prohibits NULL.
Sourcepub fn as_bytes(&self) -> Option<&[u8]>
pub fn as_bytes(&self) -> Option<&[u8]>
Get the slice of bytes representing the content of this value, not including any NUL terminator.
Any variant can be represented as a byte slice, so this method does not mutate the FzString and cannot fail.
The Null variant is represented as None.
Sourcepub fn as_bytes_nonnull(&self) -> &[u8] ⓘ
pub fn as_bytes_nonnull(&self) -> &[u8] ⓘ
Get the slice of bytes representing the content of this value, not including any NUL terminator, panicing if this is the Null Variant.
This is a simple wrapper that will panic on the Null variant. This is useful when the C API prohibits NULL.
Sourcepub unsafe fn with_ref<T, F: Fn(&FzString<'_>) -> T>(
fzstr: *const fz_string_t,
f: F,
) -> T
pub unsafe fn with_ref<T, F: Fn(&FzString<'_>) -> T>( fzstr: *const fz_string_t, f: F, ) -> T
Call the contained function with a shared reference to the FzString.
This is a wrapper around ffizz_passby::Unboxed::with_ref
.
§Safety
- fzstr must be NULL or point to a valid fz_string_t value
- no other thread may mutate the value pointed to by fzstr until with_ref returns.
Sourcepub unsafe fn with_ref_mut<T, F: Fn(&mut FzString<'_>) -> T>(
fzstr: *mut fz_string_t,
f: F,
) -> T
pub unsafe fn with_ref_mut<T, F: Fn(&mut FzString<'_>) -> T>( fzstr: *mut fz_string_t, f: F, ) -> T
Call the contained function with an exclusive reference to the FzString.
This is a wrapper around ffizz_passby::Unboxed::with_ref_mut
.
§Safety
- fzstr must be NULL or point to a valid
fz_string_t
value - no other thread may access the value pointed to by
fzstr
untilwith_ref_mut
returns.
Examples found in repository?
114pub unsafe extern "C" fn kvstore_get(
115 store: *mut kvstore_t,
116 key: *mut kvstore_string_t,
117) -> kvstore_string_t {
118 // SAFETY:
119 // - store is not NULL and valid (see docstring)
120 // - store is valid for the life of this function (documented as not threadsafe)
121 // - store will not be accessed during the life of this function (documented as not threadsafe)
122 unsafe {
123 BoxedStore::with_ref_nonnull(store, |store| {
124 // SAFETY:
125 // - key must be a valid kvstore_string_t (docstring)
126 // - key will not be accessed concurrency (type docstring)
127 match unsafe {
128 FzString::with_ref_mut(key, |key| {
129 if let Ok(Some(key)) = key.as_str() {
130 store.get(key)
131 } else {
132 None // Null key or invalid UTF-8 looks the same as key-not-found
133 }
134 })
135 } {
136 // SAFETY:
137 // - the caller will free the returned value (see docstring)
138 Some(val) => unsafe { FzString::return_val(FzString::String(val.to_string())) },
139 // SAFETY:
140 // - the caller will free the returned value (see docstring)
141 None => unsafe { FzString::return_val(FzString::Null) },
142 }
143 })
144 }
145}
146
147#[ffizz_header::item]
148#[ffizz(order = 30)]
149/// Set a value in the kvstore_t, consuming the key and value. Returns false on error.
150///
151/// # Safety
152///
153/// The store must be non-NULL and point to a valid kvstore_t.
154///
155/// The key and value must both be valid kvstore_string_t values, must not be otherwise accessed
156/// while this function executes, and are invalid when this function returns.
157///
158/// # Note
159///
160/// The kvstore API sometimes invalidates its string arguments and sometimes leaves that
161/// reponsibility to the caller, which could lead to confusion for users of the library. It's done
162/// here for example purposes only!
163///
164/// ```c
165/// bool kvstore_set(kvstore *store, kvstore_string_t *key, kvstore_string_t *value);
166/// ```
167#[no_mangle]
168pub unsafe extern "C" fn kvstore_set(
169 store: *mut kvstore_t,
170 key: *mut kvstore_string_t,
171 val: *mut kvstore_string_t,
172) -> bool {
173 // SAFETY:
174 // - store is not NULL and valid (see docstring)
175 // - store is valid for the life of this function (documented as not threadsafe)
176 // - store will not be accessed during the life of this function (documented as not threadsafe)
177 unsafe {
178 BoxedStore::with_ref_mut_nonnull(store, |store| {
179 // SAFETY:
180 // - key/val are valid kvstore_string_t's (see docstring)
181 // - key/val are not accessed concurrently (type docstring)
182 // - key/val are not uesd after function returns (see docstring)
183 let (key, val) = unsafe { (FzString::take_ptr(key), FzString::take_ptr(val)) };
184
185 if let Ok(Some(key)) = key.into_string() {
186 if let Ok(Some(val)) = val.into_string() {
187 store.set(key, val);
188 return true;
189 }
190 }
191 false
192 })
193 }
194}
195
196#[ffizz_header::item]
197#[ffizz(order = 30)]
198/// Delete a value from the kvstore_t. Returns false on error.
199///
200/// # Safety
201///
202/// The store must be non-NULL and point to a valid kvstore_t.
203///
204/// The key must be a valid kvstore_string_t, must not be otherwise accessed while this function
205/// executes, and will remain valid after this function returns.
206/// ```c
207/// bool kvstore_del(kvstore *store, kvstore_string_t *key);
208/// ```
209#[no_mangle]
210pub unsafe extern "C" fn kvstore_del(store: *mut kvstore_t, key: *mut kvstore_string_t) -> bool {
211 // SAFETY:
212 // - key must be a valid kvstore_string_t (docstring)
213 // - key will not be accessed concurrency (type docstring)
214 unsafe {
215 FzString::with_ref_mut(key, move |key| {
216 // SAFETY:
217 // - store is not NULL and valid (see docstring)
218 // - store is valid for the life of this function (documented as not threadsafe)
219 // - store will not be accessed during the life of this function (documented as not threadsafe)
220 unsafe {
221 BoxedStore::with_ref_mut_nonnull(store, move |store| {
222 if let Ok(Some(key)) = key.as_str() {
223 store.del(key);
224 true
225 } else {
226 false
227 }
228 })
229 }
230 })
231 }
232}
Sourcepub unsafe fn to_out_param(self, fzstr: *mut fz_string_t)
pub unsafe fn to_out_param(self, fzstr: *mut fz_string_t)
Initialize the value pointed to fzstr with, “moving” it into the pointer.
This is a wrapper around ffizz_passby::Unboxed::to_out_param
.
If the pointer is NULL, the value is dropped.
§Safety
- if fzstr is not NULl, then it must be aligned for fz_string_t, and must have enough space for fz_string_t.
- ownership of the string is transfered to
*fzstr
or dropped.
Sourcepub unsafe fn to_out_param_nonnull(self, fzstr: *mut fz_string_t)
pub unsafe fn to_out_param_nonnull(self, fzstr: *mut fz_string_t)
Initialize the value pointed to fzstr with, “moving” it into the pointer.
This is a wrapper around ffizz_passby::Unboxed::to_out_param_nonnull
.
If the pointer is NULL, this method will panic. Use this when the C API requires that the pointer be non-NULL.
§Safety
- fzstr must not be NULL, must be aligned for fz_string_t, and must have enough space for fz_string_t.
- ownership of the string is transfered to
*fzstr
.
Sourcepub unsafe fn return_val(self) -> fz_string_t
pub unsafe fn return_val(self) -> fz_string_t
Return a fz_string_t
transferring ownership out of the function.
This is a wrapper around ffizz_passby::Unboxed::return_val
.
§Safety
- to avoid a leak, ownership of the value must eventually be returned to Rust.
Examples found in repository?
114pub unsafe extern "C" fn kvstore_get(
115 store: *mut kvstore_t,
116 key: *mut kvstore_string_t,
117) -> kvstore_string_t {
118 // SAFETY:
119 // - store is not NULL and valid (see docstring)
120 // - store is valid for the life of this function (documented as not threadsafe)
121 // - store will not be accessed during the life of this function (documented as not threadsafe)
122 unsafe {
123 BoxedStore::with_ref_nonnull(store, |store| {
124 // SAFETY:
125 // - key must be a valid kvstore_string_t (docstring)
126 // - key will not be accessed concurrency (type docstring)
127 match unsafe {
128 FzString::with_ref_mut(key, |key| {
129 if let Ok(Some(key)) = key.as_str() {
130 store.get(key)
131 } else {
132 None // Null key or invalid UTF-8 looks the same as key-not-found
133 }
134 })
135 } {
136 // SAFETY:
137 // - the caller will free the returned value (see docstring)
138 Some(val) => unsafe { FzString::return_val(FzString::String(val.to_string())) },
139 // SAFETY:
140 // - the caller will free the returned value (see docstring)
141 None => unsafe { FzString::return_val(FzString::Null) },
142 }
143 })
144 }
145}
Sourcepub unsafe fn take(fzstr: fz_string_t) -> Self
pub unsafe fn take(fzstr: fz_string_t) -> Self
Take a fz_string_t
by value and return an owned FzString
.
This is a wrapper around ffizz_passby::Unboxed::take
.
This method is intended for C API functions that take a string by value and are documented as taking ownership of the value. However, this means that C retains an expired “copy” of the value and could lead to use-after-free errors.
Where compatible with the API design, prefer to use pointers in the C API and use
FzString::take_ptr
to ensure the old value is invalidated.
§Safety
- fzstr must be a valid
fz_string_t
value
Sourcepub unsafe fn take_ptr(fzstr: *mut fz_string_t) -> Self
pub unsafe fn take_ptr(fzstr: *mut fz_string_t) -> Self
Take a pointer to a CType and return an owned value.
This is a wrapper around ffizz_passby::Unboxed::take_ptr
.
This is intended for C API functions that take a value by reference (pointer), but still “take ownership” of the value. It leaves behind an invalid value, where any non-padding bytes of the Rust type are zeroed. This makes use-after-free errors in the C code more likely to crash instead of silently working. Which is about as good as it gets in C.
Do not pass a pointer to a Rust value to this function:
let rust_value = RustType::take_ptr(&mut c_value); // BAD!
This creates undefined behavior as Rust will assume c_value
is still initialized. Use
take
in this situation.
§Safety
- fzstr must be NULL or point to a valid fz_string_t value.
- the memory pointed to by fzstr is uninitialized when this function returns.
Examples found in repository?
168pub unsafe extern "C" fn kvstore_set(
169 store: *mut kvstore_t,
170 key: *mut kvstore_string_t,
171 val: *mut kvstore_string_t,
172) -> bool {
173 // SAFETY:
174 // - store is not NULL and valid (see docstring)
175 // - store is valid for the life of this function (documented as not threadsafe)
176 // - store will not be accessed during the life of this function (documented as not threadsafe)
177 unsafe {
178 BoxedStore::with_ref_mut_nonnull(store, |store| {
179 // SAFETY:
180 // - key/val are valid kvstore_string_t's (see docstring)
181 // - key/val are not accessed concurrently (type docstring)
182 // - key/val are not uesd after function returns (see docstring)
183 let (key, val) = unsafe { (FzString::take_ptr(key), FzString::take_ptr(val)) };
184
185 if let Ok(Some(key)) = key.into_string() {
186 if let Ok(Some(val)) = val.into_string() {
187 store.set(key, val);
188 return true;
189 }
190 }
191 false
192 })
193 }
194}