1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std;
use std::ffi::{CStr, FromBytesWithNulError};
use std::str::Utf8Error;
/// Represents a string value.
///
/// The string might or might not be zero terminated, and might or might not be
/// valid UTF-8. The accessor functions will check these properties if needed.
#[derive(Clone, Copy)]
pub struct StrValue<'d> {
inner: Inner<'d>,
}
#[derive(Clone, Copy)]
enum Inner<'d> {
BytesWithoutNul(&'d [u8]),
BytesWithNul(&'d [u8]),
Str(&'d str),
CStr(&'d CStr),
}
impl<'d> StrValue<'d> {
/// Create a StrValue referring to raw bytes, which do not include a nul-terminator.
pub fn from_bytes_without_nul(bytes: &'d [u8]) -> StrValue<'d> {
StrValue {
inner: Inner::BytesWithoutNul(bytes),
}
}
/// Create a StrValue referring to raw bytes, which include a nul-terminator.
///
/// Panics if the last byte is not a nul-terminator.
pub fn from_bytes_with_nul(bytes: &'d [u8]) -> StrValue<'d> {
assert!(bytes.last() == Some(&0));
StrValue {
inner: Inner::BytesWithNul(bytes),
}
}
/// Create a StrValue referring to a non-zero terminated UTF-8 `str`.
pub fn from_str(s: &'d str) -> StrValue<'d> {
StrValue {
inner: Inner::Str(s),
}
}
/// Create a StrValue referring to a zero-terminated C string.
pub fn from_cstr(s: &'d CStr) -> StrValue<'d> {
StrValue {
inner: Inner::CStr(s),
}
}
/// Get the raw bytes of the string, without zero terminmator.
///
/// Always works, regardless of what type of string the value refers to.
pub fn as_bytes(&self) -> &'d [u8] {
match self.inner {
Inner::BytesWithoutNul(v) => v,
Inner::BytesWithNul(v) => &v[..v.len() - 1],
Inner::Str(v) => v.as_bytes(),
Inner::CStr(v) => v.to_bytes(),
}
}
/// Get the value as `&str`.
///
/// Unless the StrValue refers to a `&str`, it is checked for valid UTF-8.
pub fn as_str(&self) -> Result<&'d str, Utf8Error> {
match self.inner {
Inner::Str(v) => Ok(v),
_ => std::str::from_utf8(self.as_bytes()),
}
}
/// Get the value as (nul-terminated) C string.
///
/// This only works when the StrValue:
///
/// - refers to a `&Cstr`,
/// - refers to raw bytes including a nul terminator, but not containing
/// any other nul bytes (this is checked), or
/// - refers to an empty string.
///
/// Otherwise, it fails.
///
/// Note that this should always work for values resulting from properly
/// encoded argdata, as long as the value doesn't contain any embedded nul
/// bytes.
pub fn as_cstr(&self) -> Result<&'d CStr, FromBytesWithNulError> {
let bytes = match self.inner {
Inner::CStr(v) => return Ok(v),
Inner::BytesWithNul(v) => v,
Inner::Str("") | Inner::BytesWithoutNul(b"") => &[0],
_ => &[], // Will trigger a missing-nul-terminator error.
};
CStr::from_bytes_with_nul(bytes)
}
}