Enum FzString

Source
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>

Source

pub fn is_null(&self) -> bool

Check if this is a Null FzString.

Source

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?
examples/kv.rs (line 129)
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}
Source

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.

Source

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.

Source

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.

Source

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?
examples/kv.rs (line 185)
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}
Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.
Source

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 until with_ref_mut returns.
Examples found in repository?
examples/kv.rs (lines 128-134)
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}
Source

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.
Source

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.
Source

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?
examples/kv.rs (line 138)
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}
Source

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
Source

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?
examples/kv.rs (line 183)
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}

Trait Implementations§

Source§

impl<'a> Debug for FzString<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'a> Default for FzString<'a>

Source§

fn default() -> FzString<'a>

Returns the “default value” for a type. Read more
Source§

impl From<&[u8]> for FzString<'static>

Source§

fn from(bytes: &[u8]) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<&str> for FzString<'static>

Source§

fn from(string: &str) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<Option<&[u8]>> for FzString<'static>

Source§

fn from(bytes: Option<&[u8]>) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<Option<&str>> for FzString<'static>

Source§

fn from(string: Option<&str>) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<Option<String>> for FzString<'static>

Source§

fn from(string: Option<String>) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<Option<Vec<u8>>> for FzString<'static>

Source§

fn from(bytes: Option<Vec<u8>>) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<String> for FzString<'static>

Source§

fn from(string: String) -> FzString<'static>

Converts to this type from the input type.
Source§

impl From<Vec<u8>> for FzString<'static>

Source§

fn from(bytes: Vec<u8>) -> FzString<'static>

Converts to this type from the input type.
Source§

impl<'a> PartialEq for FzString<'a>

Source§

fn eq(&self, other: &FzString<'a>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<'a> Eq for FzString<'a>

Source§

impl<'a> StructuralPartialEq for FzString<'a>

Auto Trait Implementations§

§

impl<'a> Freeze for FzString<'a>

§

impl<'a> RefUnwindSafe for FzString<'a>

§

impl<'a> Send for FzString<'a>

§

impl<'a> Sync for FzString<'a>

§

impl<'a> Unpin for FzString<'a>

§

impl<'a> UnwindSafe for FzString<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.