Skip to content

Commit 77ac586

Browse files
committed
Imporove BorrowedStr/BorrowedBytes ergonomic.
Implement `FromLua` and `IntoLua` for these types to allow working with them directly.
1 parent c975660 commit 77ac586

File tree

4 files changed

+222
-36
lines changed

4 files changed

+222
-36
lines changed

src/conversion.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use num_traits::cast;
1313
use crate::error::{Error, Result};
1414
use crate::function::Function;
1515
use crate::state::{Lua, RawLua};
16-
use crate::string::String;
16+
use crate::string::{BorrowedBytes, BorrowedStr, String};
1717
use crate::table::Table;
1818
use crate::thread::Thread;
1919
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
@@ -91,6 +91,90 @@ impl FromLua for String {
9191
}
9292
}
9393

94+
impl IntoLua for BorrowedStr<'_> {
95+
#[inline]
96+
fn into_lua(self, _: &Lua) -> Result<Value> {
97+
Ok(Value::String(self.borrow.into_owned()))
98+
}
99+
100+
#[inline]
101+
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
102+
lua.push_ref(&self.borrow.0);
103+
Ok(())
104+
}
105+
}
106+
107+
impl IntoLua for &BorrowedStr<'_> {
108+
#[inline]
109+
fn into_lua(self, _: &Lua) -> Result<Value> {
110+
Ok(Value::String(self.borrow.clone().into_owned()))
111+
}
112+
113+
#[inline]
114+
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
115+
lua.push_ref(&self.borrow.0);
116+
Ok(())
117+
}
118+
}
119+
120+
impl FromLua for BorrowedStr<'_> {
121+
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
122+
let s = String::from_lua(value, lua)?;
123+
let BorrowedStr { buf, _guard, .. } = BorrowedStr::try_from(&s)?;
124+
let borrow = Cow::Owned(s);
125+
Ok(Self { buf, borrow, _guard })
126+
}
127+
128+
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
129+
let s = String::from_stack(idx, lua)?;
130+
let BorrowedStr { buf, _guard, .. } = BorrowedStr::try_from(&s)?;
131+
let borrow = Cow::Owned(s);
132+
Ok(Self { buf, borrow, _guard })
133+
}
134+
}
135+
136+
impl IntoLua for BorrowedBytes<'_> {
137+
#[inline]
138+
fn into_lua(self, _: &Lua) -> Result<Value> {
139+
Ok(Value::String(self.borrow.into_owned()))
140+
}
141+
142+
#[inline]
143+
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
144+
lua.push_ref(&self.borrow.0);
145+
Ok(())
146+
}
147+
}
148+
149+
impl IntoLua for &BorrowedBytes<'_> {
150+
#[inline]
151+
fn into_lua(self, _: &Lua) -> Result<Value> {
152+
Ok(Value::String(self.borrow.clone().into_owned()))
153+
}
154+
155+
#[inline]
156+
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
157+
lua.push_ref(&self.borrow.0);
158+
Ok(())
159+
}
160+
}
161+
162+
impl FromLua for BorrowedBytes<'_> {
163+
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
164+
let s = String::from_lua(value, lua)?;
165+
let BorrowedBytes { buf, _guard, .. } = BorrowedBytes::from(&s);
166+
let borrow = Cow::Owned(s);
167+
Ok(Self { buf, borrow, _guard })
168+
}
169+
170+
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
171+
let s = String::from_stack(idx, lua)?;
172+
let BorrowedBytes { buf, _guard, .. } = BorrowedBytes::from(&s);
173+
let borrow = Cow::Owned(s);
174+
Ok(Self { buf, borrow, _guard })
175+
}
176+
}
177+
94178
impl IntoLua for Table {
95179
#[inline]
96180
fn into_lua(self, _: &Lua) -> Result<Value> {

src/string.rs

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::borrow::Borrow;
1+
use std::borrow::{Borrow, Cow};
22
use std::hash::{Hash, Hasher};
33
use std::ops::Deref;
44
use std::os::raw::{c_int, c_void};
@@ -44,13 +44,7 @@ impl String {
4444
/// ```
4545
#[inline]
4646
pub fn to_str(&self) -> Result<BorrowedStr> {
47-
let BorrowedBytes(bytes, guard) = self.as_bytes();
48-
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
49-
from: "string",
50-
to: "&str".to_string(),
51-
message: Some(e.to_string()),
52-
})?;
53-
Ok(BorrowedStr(s, guard))
47+
BorrowedStr::try_from(self)
5448
}
5549

5650
/// Converts this string to a [`StdString`].
@@ -109,19 +103,21 @@ impl String {
109103
/// ```
110104
#[inline]
111105
pub fn as_bytes(&self) -> BorrowedBytes {
112-
let (bytes, guard) = unsafe { self.to_slice() };
113-
BorrowedBytes(&bytes[..bytes.len() - 1], guard)
106+
BorrowedBytes::from(self)
114107
}
115108

116109
/// Get the bytes that make up this string, including the trailing nul byte.
117110
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
118-
let (bytes, guard) = unsafe { self.to_slice() };
119-
BorrowedBytes(bytes, guard)
111+
let BorrowedBytes { buf, borrow, _guard } = BorrowedBytes::from(self);
112+
// Include the trailing nul byte (it's always present but excluded by default)
113+
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
114+
BorrowedBytes { buf, borrow, _guard }
120115
}
121116

117+
// Does not return the terminating nul byte
122118
unsafe fn to_slice(&self) -> (&[u8], Lua) {
123119
let lua = self.0.lua.upgrade();
124-
let slice = unsafe {
120+
let slice = {
125121
let rawlua = lua.lock();
126122
let ref_thread = rawlua.ref_thread();
127123

@@ -134,7 +130,7 @@ impl String {
134130
// string type
135131
let mut size = 0;
136132
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
137-
slice::from_raw_parts(data as *const u8, size + 1)
133+
slice::from_raw_parts(data as *const u8, size)
138134
};
139135
(slice, lua)
140136
}
@@ -238,40 +234,44 @@ impl fmt::Display for Display<'_> {
238234
}
239235

240236
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
241-
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
237+
pub struct BorrowedStr<'a> {
238+
pub(crate) buf: *const str,
239+
pub(crate) borrow: Cow<'a, String>,
240+
pub(crate) _guard: Lua,
241+
}
242242

243243
impl Deref for BorrowedStr<'_> {
244244
type Target = str;
245245

246246
#[inline(always)]
247247
fn deref(&self) -> &str {
248-
self.0
248+
unsafe { &*self.buf }
249249
}
250250
}
251251

252252
impl Borrow<str> for BorrowedStr<'_> {
253253
#[inline(always)]
254254
fn borrow(&self) -> &str {
255-
self.0
255+
self.deref()
256256
}
257257
}
258258

259259
impl AsRef<str> for BorrowedStr<'_> {
260260
#[inline(always)]
261261
fn as_ref(&self) -> &str {
262-
self.0
262+
self.deref()
263263
}
264264
}
265265

266266
impl fmt::Display for BorrowedStr<'_> {
267267
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268-
self.0.fmt(f)
268+
self.as_ref().fmt(f)
269269
}
270270
}
271271

272272
impl fmt::Debug for BorrowedStr<'_> {
273273
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
274-
self.0.fmt(f)
274+
self.as_ref().fmt(f)
275275
}
276276
}
277277

@@ -280,7 +280,7 @@ where
280280
T: AsRef<str>,
281281
{
282282
fn eq(&self, other: &T) -> bool {
283-
self.0 == other.as_ref()
283+
self.as_ref() == other.as_ref()
284284
}
285285
}
286286

@@ -291,45 +291,64 @@ where
291291
T: AsRef<str>,
292292
{
293293
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
294-
self.0.partial_cmp(other.as_ref())
294+
self.as_ref().partial_cmp(other.as_ref())
295295
}
296296
}
297297

298298
impl Ord for BorrowedStr<'_> {
299299
fn cmp(&self, other: &Self) -> cmp::Ordering {
300-
self.0.cmp(other.0)
300+
self.as_ref().cmp(other.as_ref())
301+
}
302+
}
303+
304+
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
305+
type Error = Error;
306+
307+
#[inline]
308+
fn try_from(value: &'a String) -> Result<Self> {
309+
let BorrowedBytes { buf, borrow, _guard } = BorrowedBytes::from(value);
310+
let buf = str::from_utf8(unsafe { &*buf }).map_err(|e| Error::FromLuaConversionError {
311+
from: "string",
312+
to: "&str".to_string(),
313+
message: Some(e.to_string()),
314+
})?;
315+
Ok(Self { buf, borrow, _guard })
301316
}
302317
}
303318

304319
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
305-
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
320+
pub struct BorrowedBytes<'a> {
321+
pub(crate) buf: *const [u8],
322+
pub(crate) borrow: Cow<'a, String>,
323+
pub(crate) _guard: Lua,
324+
}
306325

307326
impl Deref for BorrowedBytes<'_> {
308327
type Target = [u8];
309328

310329
#[inline(always)]
311330
fn deref(&self) -> &[u8] {
312-
self.0
331+
unsafe { &*self.buf }
313332
}
314333
}
315334

316335
impl Borrow<[u8]> for BorrowedBytes<'_> {
317336
#[inline(always)]
318337
fn borrow(&self) -> &[u8] {
319-
self.0
338+
self.deref()
320339
}
321340
}
322341

323342
impl AsRef<[u8]> for BorrowedBytes<'_> {
324343
#[inline(always)]
325344
fn as_ref(&self) -> &[u8] {
326-
self.0
345+
self.deref()
327346
}
328347
}
329348

330349
impl fmt::Debug for BorrowedBytes<'_> {
331350
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332-
self.0.fmt(f)
351+
self.as_ref().fmt(f)
333352
}
334353
}
335354

@@ -338,7 +357,7 @@ where
338357
T: AsRef<[u8]>,
339358
{
340359
fn eq(&self, other: &T) -> bool {
341-
self.0 == other.as_ref()
360+
self.as_ref() == other.as_ref()
342361
}
343362
}
344363

@@ -349,22 +368,31 @@ where
349368
T: AsRef<[u8]>,
350369
{
351370
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
352-
self.0.partial_cmp(other.as_ref())
371+
self.as_ref().partial_cmp(other.as_ref())
353372
}
354373
}
355374

356375
impl Ord for BorrowedBytes<'_> {
357376
fn cmp(&self, other: &Self) -> cmp::Ordering {
358-
self.0.cmp(other.0)
377+
self.as_ref().cmp(other.as_ref())
359378
}
360379
}
361380

362-
impl<'a> IntoIterator for BorrowedBytes<'a> {
381+
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
363382
type Item = &'a u8;
364383
type IntoIter = slice::Iter<'a, u8>;
365384

366385
fn into_iter(self) -> Self::IntoIter {
367-
self.0.iter()
386+
self.iter()
387+
}
388+
}
389+
390+
impl<'a> From<&'a String> for BorrowedBytes<'a> {
391+
#[inline]
392+
fn from(value: &'a String) -> Self {
393+
let (buf, _guard) = unsafe { value.to_slice() };
394+
let borrow = Cow::Borrowed(value);
395+
Self { buf, borrow, _guard }
368396
}
369397
}
370398

0 commit comments

Comments
 (0)