Skip to main content

low_luajit/lua/type/
string.rs

1use core::{ffi::CStr, ptr, slice, str};
2use crate::{AmountReturned, LuaState, ffi, map_ptr};
3
4/// String related functions.
5impl LuaState {
6	/// Returns `true` if the value at the given acceptable `index` is a string, `false` otherwise.
7	#[inline]
8	#[must_use]
9	pub fn is_string(&self, index: i32) -> bool {
10		// SAFETY: self.0 is always valid.
11		unsafe { ffi::lua_isstring(self.0, index) != 0 }
12	}
13
14	/// Converts the value at the given acceptable `index` to a string and returns it as a string slice.
15	///
16	/// Only valid UTF-8 strings or numbers are accepted. Anything else will return `None`.
17	///
18	/// This function *does* affect the element in the stack itself:
19	/// numbers will be replaced by the converted string of said number.
20	#[inline]
21	#[must_use]
22	pub fn to_string(&self, index: i32) -> Option<&str> {
23		str::from_utf8(self.to_byte_string(index)?).ok()
24	}
25
26	/// 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.
27	/// This conversion usually leads to UB soon after, which is pretty bad.
28	///
29	/// This function checks if the index is invalid in this specific context and panics if it is.
30	///
31	/// As this panic is very niche, it is currently undocumented. This might change in the future.
32	#[inline]
33	fn assert_string_index(&self, index: i32) {
34		assert!(
35			index.is_positive() || index <= Self::REGISTRY_INDEX || self.is_index_valid(index) || !self.is_number(index),
36			concat!(
37				"cannot convert the number at index {} to a string, as there are only {} elements\n",
38				"(converting this out of bounds number would very likely cause UB)"
39			),
40			index,
41			self.get_top()
42		);
43	}
44
45	/// Converts the value at the given acceptable `index` to a string and returns it as a byte slice.
46	///
47	/// Only strings or numbers are accepted. Anything else will return `None`.
48	///
49	/// This function *does* affect the element in the stack itself:
50	/// numbers will be replaced by the converted string of said number.
51	#[inline]
52	#[must_use]
53	pub fn to_byte_string(&self, index: i32) -> Option<&[u8]> {
54		self.assert_string_index(index);
55
56		// SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
57		// and lua_tolstring returns valid for len amount of values string pointers when not null.
58		let mut len = 0;
59		let string = unsafe { ffi::lua_tolstring(self.0, index, &raw mut len) };
60		map_ptr!(string, unsafe { slice::from_raw_parts(string.cast(), len) })
61	}
62
63	/// Converts the value at the given acceptable `index` to a string and returns it as a `CStr`.
64	///
65	/// Only strings or numbers are accepted. Anything else will return `None`.
66	///
67	/// This function *does* affect the element in the stack itself:
68	/// numbers will be replaced by the converted string of said number.
69	#[inline]
70	#[must_use]
71	pub fn to_cstring(&self, index: i32) -> Option<&CStr> {
72		self.assert_string_index(index);
73
74		// SAFETY: self.0 is always valid and lua_tostring returns valid nul-terminated string pointers when not null.
75		let cstring = unsafe { ffi::lua_tostring(self.0, index) };
76		map_ptr!(cstring, unsafe { CStr::from_ptr(cstring) })
77	}
78
79	/// Pushes a copy of the given `string` onto the stack.
80	#[inline]
81	pub fn push_string(&mut self, string: impl AsRef<[u8]>) {
82		self.push_byte_string(string.as_ref());
83	}
84
85	/// Pushes a copy of the given `byte_string` onto the stack.
86	#[inline]
87	pub fn push_byte_string(&mut self, byte_string: &[u8]) {
88		// SAFETY: self.0is always valid, the pointer remains valid for the duration of this function,
89		// and the length is correct as it's returned from the array itself.
90		unsafe { ffi::lua_pushlstring(self.0, byte_string.as_ptr().cast(), byte_string.len()); }
91	}
92
93	/// Pushes a copy of the given `cstring` onto the stack.
94	///
95	/// If `cstring` is `None`, `nil` will be pushed onto the stack instead.
96	#[inline]
97	pub fn push_cstring<'a>(&mut self, cstring: impl Into<Option<&'a CStr>>) {
98		// SAFETY: self.0 is always valid and the pointer either remains valid for the duration of this function
99		// or is null which lua_pushstring accepts.
100		unsafe { ffi::lua_pushstring(self.0, cstring.into().map_or(core::ptr::null(), CStr::as_ptr)); }
101	}
102
103	/// 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.
104	///
105	/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
106	///
107	/// This function has the same caveats as [`LuaState::to_string`].
108	///
109	/// # Lua Errors
110	///
111	/// Intentionally errors if the check fails.
112	#[inline]
113	pub fn check_string(&self, arg_n: i32) -> &str {
114		match str::from_utf8(self.check_byte_string(arg_n)) {
115			Ok(string) => string,
116			Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
117		}
118	}
119
120	/// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a byte slice.
121	///
122	/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
123	///
124	/// This function has the same caveats as [`LuaState::to_cstring`].
125	///
126	/// # Lua Errors
127	///
128	/// Intentionally errors if the check fails.
129	#[inline]
130	pub fn check_byte_string(&self, arg_n: i32) -> &[u8] {
131		self.assert_string_index(arg_n);
132
133		// SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
134		// and string should always be non-null and valid for len amount of values.
135		let mut len = 0;
136		let string = unsafe { ffi::luaL_checklstring(self.0, arg_n, &raw mut len) };
137		unsafe { slice::from_raw_parts(string.cast(), len) }
138	}
139
140	/// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a `CStr`.
141	///
142	/// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
143	///
144	/// This function has the same caveats as [`LuaState::to_byte_string`].
145	///
146	/// # Lua Errors
147	///
148	/// Intentionally errors if the check fails.
149	#[inline]
150	pub fn check_cstring(&self, arg_n: i32) -> &CStr {
151		self.assert_string_index(arg_n);
152
153		// SAFETY: self.0 is always valid and the returned pointer should be non-null, nul terminated, and valid for all values.
154		unsafe { CStr::from_ptr(ffi::luaL_checkstring(self.0, arg_n)) }
155	}
156
157	/// Behaves exactly as [`LuaState::check_string`],
158	/// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
159	#[inline]
160	pub fn opt_string<'a>(&'a self, arg_n: i32, default: &'a str) -> &'a str {
161		self.assert_string_index(arg_n);
162
163		// SAFETY: self.0 is always valid, luaL_optlstring accepts a null pointer for the default value,
164		// and len is initialized right before passing its pointer.
165		let mut len = 0;
166		let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, ptr::null(), &raw mut len) };
167
168		if string.is_null() {
169			return default;
170		}
171
172		// SAFETY: string should always be non-null and valid for len amount of values.
173		match str::from_utf8(unsafe { slice::from_raw_parts(string.cast(), len) }) {
174			Ok(string) => string,
175			Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
176		}
177	}
178
179	/// Behaves exactly as [`LuaState::check_byte_string`],
180	/// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
181	#[inline]
182	pub fn opt_byte_string<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a [u8] {
183		self.assert_string_index(arg_n);
184
185		// SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
186		// and string should always be non-null and valid for len amount of values.
187		let mut len = 0;
188		let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, default.as_ptr(), &raw mut len) };
189		unsafe { slice::from_raw_parts(string.cast(), len) }
190	}
191
192	/// Behaves exactly as [`LuaState::check_cstring`],
193	/// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
194	#[inline]
195	pub fn opt_cstring<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a CStr {
196		self.assert_string_index(arg_n);
197
198		// SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
199		// and string should always be non-null and valid for len amount of values.
200		unsafe { CStr::from_ptr(ffi::luaL_optstring(self.0, arg_n, default.as_ptr())) }
201	}
202
203	/// Finishes the currently called [`CFunction`], returning the given `string` to the caller.
204	///
205	/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
206	///
207	/// [`CFunction`]: ./type.CFunction.html
208	#[inline]
209	pub fn return_string(self, string: impl AsRef<[u8]>) -> AmountReturned {
210		self.return_byte_string(string.as_ref())
211	}
212
213	/// Finishes the currently called [`CFunction`], returning the given `byte_string` to the caller.
214	///
215	/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
216	///
217	/// [`CFunction`]: ./type.CFunction.html
218	#[inline]
219	pub fn return_byte_string(mut self, byte_string: &[u8]) -> AmountReturned {
220		self.push_byte_string(byte_string);
221
222		// SAFETY: There should always be at least one element in the stack.
223		unsafe { AmountReturned::unchecked(1) }
224	}
225
226	/// Finishes the currently called [`CFunction`], returning the given `cstring` to the caller.
227	///
228	/// If `cstring` is `None`, `nil` will be returned to the caller instead.
229	///
230	/// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
231	///
232	/// [`CFunction`]: ./type.CFunction.html
233	#[inline]
234	pub fn return_cstring<'a>(mut self, cstring: impl Into<Option<&'a CStr>>) -> AmountReturned {
235		self.push_cstring(cstring);
236
237		// SAFETY: There should always be at least one element in the stack.
238		unsafe { AmountReturned::unchecked(1) }
239	}
240
241	/// Creates a copy of `string` where every occurrence of `pattern` is replaced by `replacement`.
242	///
243	/// The resulting string is both pushed onto the stack and returned.
244	#[inline]
245	pub fn gsub(&mut self, string: &CStr, pattern: &CStr, replacement: &CStr) -> &CStr {
246		// SAFETY: self.0 and string, pattern, and replacement's pointers are always valid and luaL_gsub always returns a valid string pointer.
247		unsafe { CStr::from_ptr(ffi::luaL_gsub(self.0, string.as_ptr(), pattern.as_ptr(), replacement.as_ptr())) }
248	}
249}