Skip to content

Commit fca38a6

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

File tree

4 files changed

+229
-37
lines changed

4 files changed

+229
-37
lines changed

src/conversion.rs

+90-2
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ use std::hash::{BuildHasher, Hash};
55
use std::os::raw::c_int;
66
use std::path::{Path, PathBuf};
77
use std::string::String as StdString;
8-
use std::{slice, str};
8+
use std::{mem, slice, str};
99

1010
use bstr::{BStr, BString, ByteSlice, ByteVec};
1111
use num_traits::cast;
1212

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,94 @@ 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, _lua, .. } = BorrowedStr::try_from(&s)?;
124+
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
125+
let borrow = Cow::Owned(s);
126+
Ok(Self { buf, borrow, _lua })
127+
}
128+
129+
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
130+
let s = String::from_stack(idx, lua)?;
131+
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
132+
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
133+
let borrow = Cow::Owned(s);
134+
Ok(Self { buf, borrow, _lua })
135+
}
136+
}
137+
138+
impl IntoLua for BorrowedBytes<'_> {
139+
#[inline]
140+
fn into_lua(self, _: &Lua) -> Result<Value> {
141+
Ok(Value::String(self.borrow.into_owned()))
142+
}
143+
144+
#[inline]
145+
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
146+
lua.push_ref(&self.borrow.0);
147+
Ok(())
148+
}
149+
}
150+
151+
impl IntoLua for &BorrowedBytes<'_> {
152+
#[inline]
153+
fn into_lua(self, _: &Lua) -> Result<Value> {
154+
Ok(Value::String(self.borrow.clone().into_owned()))
155+
}
156+
157+
#[inline]
158+
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
159+
lua.push_ref(&self.borrow.0);
160+
Ok(())
161+
}
162+
}
163+
164+
impl FromLua for BorrowedBytes<'_> {
165+
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
166+
let s = String::from_lua(value, lua)?;
167+
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
168+
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
169+
let borrow = Cow::Owned(s);
170+
Ok(Self { buf, borrow, _lua })
171+
}
172+
173+
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
174+
let s = String::from_stack(idx, lua)?;
175+
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
176+
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
177+
let borrow = Cow::Owned(s);
178+
Ok(Self { buf, borrow, _lua })
179+
}
180+
}
181+
94182
impl IntoLua for Table {
95183
#[inline]
96184
fn into_lua(self, _: &Lua) -> Result<Value> {

src/string.rs

+63-33
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, _lua } = 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, _lua }
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,45 @@ 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+
// `buf` points to a readonly memory managed by Lua
239+
pub(crate) buf: &'a str,
240+
pub(crate) borrow: Cow<'a, String>,
241+
pub(crate) _lua: Lua,
242+
}
242243

243244
impl Deref for BorrowedStr<'_> {
244245
type Target = str;
245246

246247
#[inline(always)]
247248
fn deref(&self) -> &str {
248-
self.0
249+
self.buf
249250
}
250251
}
251252

252253
impl Borrow<str> for BorrowedStr<'_> {
253254
#[inline(always)]
254255
fn borrow(&self) -> &str {
255-
self.0
256+
self.buf
256257
}
257258
}
258259

259260
impl AsRef<str> for BorrowedStr<'_> {
260261
#[inline(always)]
261262
fn as_ref(&self) -> &str {
262-
self.0
263+
self.buf
263264
}
264265
}
265266

266267
impl fmt::Display for BorrowedStr<'_> {
267268
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268-
self.0.fmt(f)
269+
self.buf.fmt(f)
269270
}
270271
}
271272

272273
impl fmt::Debug for BorrowedStr<'_> {
273274
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
274-
self.0.fmt(f)
275+
self.buf.fmt(f)
275276
}
276277
}
277278

@@ -280,7 +281,7 @@ where
280281
T: AsRef<str>,
281282
{
282283
fn eq(&self, other: &T) -> bool {
283-
self.0 == other.as_ref()
284+
self.buf == other.as_ref()
284285
}
285286
}
286287

@@ -291,45 +292,65 @@ where
291292
T: AsRef<str>,
292293
{
293294
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
294-
self.0.partial_cmp(other.as_ref())
295+
self.buf.partial_cmp(other.as_ref())
295296
}
296297
}
297298

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

304320
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
305-
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
321+
pub struct BorrowedBytes<'a> {
322+
// `buf` points to a readonly memory managed by Lua
323+
pub(crate) buf: &'a [u8],
324+
pub(crate) borrow: Cow<'a, String>,
325+
pub(crate) _lua: Lua,
326+
}
306327

307328
impl Deref for BorrowedBytes<'_> {
308329
type Target = [u8];
309330

310331
#[inline(always)]
311332
fn deref(&self) -> &[u8] {
312-
self.0
333+
self.buf
313334
}
314335
}
315336

316337
impl Borrow<[u8]> for BorrowedBytes<'_> {
317338
#[inline(always)]
318339
fn borrow(&self) -> &[u8] {
319-
self.0
340+
self.buf
320341
}
321342
}
322343

323344
impl AsRef<[u8]> for BorrowedBytes<'_> {
324345
#[inline(always)]
325346
fn as_ref(&self) -> &[u8] {
326-
self.0
347+
self.buf
327348
}
328349
}
329350

330351
impl fmt::Debug for BorrowedBytes<'_> {
331352
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332-
self.0.fmt(f)
353+
self.buf.fmt(f)
333354
}
334355
}
335356

@@ -338,7 +359,7 @@ where
338359
T: AsRef<[u8]>,
339360
{
340361
fn eq(&self, other: &T) -> bool {
341-
self.0 == other.as_ref()
362+
self.buf == other.as_ref()
342363
}
343364
}
344365

@@ -349,22 +370,31 @@ where
349370
T: AsRef<[u8]>,
350371
{
351372
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
352-
self.0.partial_cmp(other.as_ref())
373+
self.buf.partial_cmp(other.as_ref())
353374
}
354375
}
355376

356377
impl Ord for BorrowedBytes<'_> {
357378
fn cmp(&self, other: &Self) -> cmp::Ordering {
358-
self.0.cmp(other.0)
379+
self.buf.cmp(other.buf)
359380
}
360381
}
361382

362-
impl<'a> IntoIterator for BorrowedBytes<'a> {
383+
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
363384
type Item = &'a u8;
364385
type IntoIter = slice::Iter<'a, u8>;
365386

366387
fn into_iter(self) -> Self::IntoIter {
367-
self.0.iter()
388+
self.iter()
389+
}
390+
}
391+
392+
impl<'a> From<&'a String> for BorrowedBytes<'a> {
393+
#[inline]
394+
fn from(value: &'a String) -> Self {
395+
let (buf, _lua) = unsafe { value.to_slice() };
396+
let borrow = Cow::Borrowed(value);
397+
Self { buf, borrow, _lua }
368398
}
369399
}
370400

0 commit comments

Comments
 (0)