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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
use core::{ffi::CStr, ptr, slice, str};
use crate::{AmountReturned, LuaState, ffi, map_ptr};
/// String related functions.
impl LuaState {
/// Returns `true` if the value at the given acceptable `index` is a string (or number convertible to string), `false` otherwise.
#[inline]
#[must_use]
pub fn is_string(&self, index: i32) -> bool {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_isstring(self.0, index) != 0 }
}
/// Converts the value at the given acceptable `index` to a string and returns it as a string slice.
///
/// Only valid UTF-8 strings or numbers are accepted. Anything else will return `None`.
///
/// This function *does* affect the element in the stack itself:
/// numbers will be replaced by the converted string of said number.
///
/// To instead obtain a printable representation of any value, see [`LuaState::display`].
#[inline]
#[must_use]
pub fn to_string(&self, index: i32) -> Option<&str> {
str::from_utf8(self.to_byte_string(index)?).ok()
}
/// Indices that are not positive, not a pseudo-index, and go out of bounds may convert a number that's used elsewhere to a string.
/// This conversion usually leads to UB soon after, which is pretty bad.
///
/// This function checks if the index is invalid in this specific context and panics if it is.
///
/// As this panic is very niche, it is currently undocumented. This may change in the future.
#[inline]
pub(crate) fn assert_string_index(&self, index: i32) {
assert!(
index.is_positive() || index <= Self::REGISTRY_INDEX || self.is_index_valid(index) || !self.is_number(index),
concat!(
"cannot convert the number at index {} to a string, as there are only {} elements\n",
"(converting this out of bounds number would very likely cause UB)"
),
index,
self.get_top()
);
}
/// Converts the value at the given acceptable `index` to a string and returns it as a byte slice.
///
/// Only strings or numbers are accepted. Anything else will return `None`.
///
/// This function *does* affect the element in the stack itself:
/// numbers will be replaced by the converted string of said number.
#[inline]
#[must_use]
pub fn to_byte_string(&self, index: i32) -> Option<&[u8]> {
self.assert_string_index(index);
// SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
// and lua_tolstring returns valid for len amount of values string pointers when not null.
let mut len = 0;
let string = unsafe { ffi::lua_tolstring(self.0, index, &raw mut len) };
map_ptr!(string, unsafe { slice::from_raw_parts(string.cast(), len) })
}
/// Converts the value at the given acceptable `index` to a string and returns it as a `CStr`.
///
/// Only strings or numbers are accepted. Anything else will return `None`.
///
/// This function *does* affect the element in the stack itself:
/// numbers will be replaced by the converted string of said number.
#[inline]
#[must_use]
pub fn to_cstring(&self, index: i32) -> Option<&CStr> {
self.assert_string_index(index);
// SAFETY: self.0 is always valid and lua_tostring returns valid nul-terminated string pointers when not null.
let cstring = unsafe { ffi::lua_tostring(self.0, index) };
map_ptr!(cstring, unsafe { CStr::from_ptr(cstring) })
}
/// Pushes a copy of the given `string` onto the stack.
#[inline]
pub fn push_string(&mut self, string: impl AsRef<[u8]>) {
self.push_byte_string(string.as_ref());
}
/// Pushes a copy of the given `byte_string` onto the stack.
#[inline]
pub fn push_byte_string(&mut self, byte_string: &[u8]) {
// SAFETY: self.0is always valid, the pointer remains valid for the duration of this function,
// and the length is correct as it's returned from the array itself.
unsafe { ffi::lua_pushlstring(self.0, byte_string.as_ptr().cast(), byte_string.len()); }
}
/// Pushes a copy of the given `cstring` onto the stack.
///
/// If `cstring` is `None`, `nil` will be pushed onto the stack instead.
#[inline]
pub fn push_cstring<'a>(&mut self, cstring: impl Into<Option<&'a CStr>>) {
// SAFETY: self.0 is always valid and the pointer either remains valid for the duration of this function
// or is null which lua_pushstring accepts.
unsafe { ffi::lua_pushstring(self.0, cstring.into().map_or(core::ptr::null(), CStr::as_ptr)); }
}
/// Checks if there's a valid UTF-8 string (or number convertible to string) at the given index `arg_n` and returns it as a string slice.
///
/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
///
/// This function has the same caveats as [`LuaState::to_string`].
///
/// # Lua Errors
///
/// Intentionally errors if the check fails.
#[inline]
pub fn check_string(&self, arg_n: i32) -> &str {
match str::from_utf8(self.check_byte_string(arg_n)) {
Ok(string) => string,
Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
}
}
/// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a byte slice.
///
/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
///
/// This function has the same caveats as [`LuaState::to_cstring`].
///
/// # Lua Errors
///
/// Intentionally errors if the check fails.
#[inline]
pub fn check_byte_string(&self, arg_n: i32) -> &[u8] {
self.assert_string_index(arg_n);
// SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
// and string should always be non-null and valid for len amount of values.
let mut len = 0;
let string = unsafe { ffi::luaL_checklstring(self.0, arg_n, &raw mut len) };
unsafe { slice::from_raw_parts(string.cast(), len) }
}
/// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a `CStr`.
///
/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
///
/// This function has the same caveats as [`LuaState::to_byte_string`].
///
/// # Lua Errors
///
/// Intentionally errors if the check fails.
#[inline]
pub fn check_cstring(&self, arg_n: i32) -> &CStr {
self.assert_string_index(arg_n);
// SAFETY: self.0 is always valid and the returned pointer should be non-null, nul terminated, and valid for all values.
unsafe { CStr::from_ptr(ffi::luaL_checkstring(self.0, arg_n)) }
}
/// Behaves exactly as [`LuaState::check_string`],
/// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
#[inline]
pub fn opt_string<'a>(&'a self, arg_n: i32, default: &'a str) -> &'a str {
self.assert_string_index(arg_n);
// SAFETY: self.0 is always valid, luaL_optlstring accepts a null pointer for the default value,
// and len is initialized right before passing its pointer.
let mut len = 0;
let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, ptr::null(), &raw mut len) };
if string.is_null() {
return default;
}
// SAFETY: string should always be non-null and valid for len amount of values.
match str::from_utf8(unsafe { slice::from_raw_parts(string.cast(), len) }) {
Ok(string) => string,
Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
}
}
/// Behaves exactly as [`LuaState::check_byte_string`],
/// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
#[inline]
pub fn opt_byte_string<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a [u8] {
self.assert_string_index(arg_n);
// SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
// and string should always be non-null and valid for len amount of values.
let mut len = 0;
let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, default.as_ptr(), &raw mut len) };
unsafe { slice::from_raw_parts(string.cast(), len) }
}
/// Behaves exactly as [`LuaState::check_cstring`],
/// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
#[inline]
pub fn opt_cstring<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a CStr {
self.assert_string_index(arg_n);
// SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
// and string should always be non-null and valid for len amount of values.
unsafe { CStr::from_ptr(ffi::luaL_optstring(self.0, arg_n, default.as_ptr())) }
}
/// Finishes the currently called [`CFunction`], returning the given `string` to the caller.
///
/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
///
/// [`CFunction`]: ./type.CFunction.html
#[inline]
pub fn return_string(self, string: impl AsRef<[u8]>) -> AmountReturned {
self.return_byte_string(string.as_ref())
}
/// Finishes the currently called [`CFunction`], returning the given `byte_string` to the caller.
///
/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
///
/// [`CFunction`]: ./type.CFunction.html
#[inline]
pub fn return_byte_string(mut self, byte_string: &[u8]) -> AmountReturned {
self.push_byte_string(byte_string);
// SAFETY: There should always be at least one element in the stack.
unsafe { AmountReturned::unchecked(1) }
}
/// Finishes the currently called [`CFunction`], returning the given `cstring` to the caller.
///
/// If `cstring` is `None`, `nil` will be returned to the caller instead.
///
/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
///
/// [`CFunction`]: ./type.CFunction.html
#[inline]
pub fn return_cstring<'a>(mut self, cstring: impl Into<Option<&'a CStr>>) -> AmountReturned {
self.push_cstring(cstring);
// SAFETY: There should always be at least one element in the stack.
unsafe { AmountReturned::unchecked(1) }
}
/// Creates a copy of `string` where every occurrence of `pattern` is replaced by `replacement`.
///
/// The resulting string is both pushed onto the stack and returned.
#[inline]
pub fn gsub(&mut self, string: &CStr, pattern: &CStr, replacement: &CStr) -> &CStr {
// SAFETY: self.0 and string, pattern, and replacement's pointers are always valid and luaL_gsub always returns a valid string pointer.
unsafe { CStr::from_ptr(ffi::luaL_gsub(self.0, string.as_ptr(), pattern.as_ptr(), replacement.as_ptr())) }
}
}