Skip to content

Commit

Permalink
Imporove BorrowedStr/BorrowedBytes ergonomic.
Browse files Browse the repository at this point in the history
Implement `FromLua` and `IntoLua` for these types to allow working with them directly.
  • Loading branch information
khvzak committed Jan 30, 2025
1 parent cdd6a99 commit fca38a6
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 37 deletions.
92 changes: 90 additions & 2 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use std::hash::{BuildHasher, Hash};
use std::os::raw::c_int;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use std::{slice, str};
use std::{mem, slice, str};

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

use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, RawLua};
use crate::string::String;
use crate::string::{BorrowedBytes, BorrowedStr, String};
use crate::table::Table;
use crate::thread::Thread;
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
Expand Down Expand Up @@ -91,6 +91,94 @@ impl FromLua for String {
}
}

impl IntoLua for BorrowedStr<'_> {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::String(self.borrow.into_owned()))
}

#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_ref(&self.borrow.0);
Ok(())
}
}

impl IntoLua for &BorrowedStr<'_> {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::String(self.borrow.clone().into_owned()))
}

#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_ref(&self.borrow.0);
Ok(())
}
}

impl FromLua for BorrowedStr<'_> {
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let s = String::from_lua(value, lua)?;
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
let borrow = Cow::Owned(s);
Ok(Self { buf, borrow, _lua })
}

unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let s = String::from_stack(idx, lua)?;
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
let borrow = Cow::Owned(s);
Ok(Self { buf, borrow, _lua })
}
}

impl IntoLua for BorrowedBytes<'_> {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::String(self.borrow.into_owned()))
}

#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_ref(&self.borrow.0);
Ok(())
}
}

impl IntoLua for &BorrowedBytes<'_> {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::String(self.borrow.clone().into_owned()))
}

#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_ref(&self.borrow.0);
Ok(())
}
}

impl FromLua for BorrowedBytes<'_> {
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let s = String::from_lua(value, lua)?;
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
let borrow = Cow::Owned(s);
Ok(Self { buf, borrow, _lua })
}

unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let s = String::from_stack(idx, lua)?;
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
let borrow = Cow::Owned(s);
Ok(Self { buf, borrow, _lua })
}
}

impl IntoLua for Table {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Expand Down
96 changes: 63 additions & 33 deletions src/string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::borrow::Borrow;
use std::borrow::{Borrow, Cow};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::os::raw::{c_int, c_void};
Expand Down Expand Up @@ -44,13 +44,7 @@ impl String {
/// ```
#[inline]
pub fn to_str(&self) -> Result<BorrowedStr> {
let BorrowedBytes(bytes, guard) = self.as_bytes();
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str".to_string(),
message: Some(e.to_string()),
})?;
Ok(BorrowedStr(s, guard))
BorrowedStr::try_from(self)
}

/// Converts this string to a [`StdString`].
Expand Down Expand Up @@ -109,19 +103,21 @@ impl String {
/// ```
#[inline]
pub fn as_bytes(&self) -> BorrowedBytes {
let (bytes, guard) = unsafe { self.to_slice() };
BorrowedBytes(&bytes[..bytes.len() - 1], guard)
BorrowedBytes::from(self)
}

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

// Does not return the terminating nul byte
unsafe fn to_slice(&self) -> (&[u8], Lua) {
let lua = self.0.lua.upgrade();
let slice = unsafe {
let slice = {
let rawlua = lua.lock();
let ref_thread = rawlua.ref_thread();

Expand All @@ -134,7 +130,7 @@ impl String {
// string type
let mut size = 0;
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
slice::from_raw_parts(data as *const u8, size + 1)
slice::from_raw_parts(data as *const u8, size)
};
(slice, lua)
}
Expand Down Expand Up @@ -238,40 +234,45 @@ impl fmt::Display for Display<'_> {
}

/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
pub struct BorrowedStr<'a> {
// `buf` points to a readonly memory managed by Lua
pub(crate) buf: &'a str,
pub(crate) borrow: Cow<'a, String>,
pub(crate) _lua: Lua,
}

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

#[inline(always)]
fn deref(&self) -> &str {
self.0
self.buf
}
}

impl Borrow<str> for BorrowedStr<'_> {
#[inline(always)]
fn borrow(&self) -> &str {
self.0
self.buf
}
}

impl AsRef<str> for BorrowedStr<'_> {
#[inline(always)]
fn as_ref(&self) -> &str {
self.0
self.buf
}
}

impl fmt::Display for BorrowedStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
self.buf.fmt(f)
}
}

impl fmt::Debug for BorrowedStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
self.buf.fmt(f)
}
}

Expand All @@ -280,7 +281,7 @@ where
T: AsRef<str>,
{
fn eq(&self, other: &T) -> bool {
self.0 == other.as_ref()
self.buf == other.as_ref()
}
}

Expand All @@ -291,45 +292,65 @@ where
T: AsRef<str>,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.0.partial_cmp(other.as_ref())
self.buf.partial_cmp(other.as_ref())
}
}

impl Ord for BorrowedStr<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(other.0)
self.buf.cmp(other.buf)
}
}

impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
type Error = Error;

#[inline]
fn try_from(value: &'a String) -> Result<Self> {
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str".to_string(),
message: Some(e.to_string()),
})?;
Ok(Self { buf, borrow, _lua })
}
}

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

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

#[inline(always)]
fn deref(&self) -> &[u8] {
self.0
self.buf
}
}

impl Borrow<[u8]> for BorrowedBytes<'_> {
#[inline(always)]
fn borrow(&self) -> &[u8] {
self.0
self.buf
}
}

impl AsRef<[u8]> for BorrowedBytes<'_> {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.0
self.buf
}
}

impl fmt::Debug for BorrowedBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
self.buf.fmt(f)
}
}

Expand All @@ -338,7 +359,7 @@ where
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
self.0 == other.as_ref()
self.buf == other.as_ref()
}
}

Expand All @@ -349,22 +370,31 @@ where
T: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.0.partial_cmp(other.as_ref())
self.buf.partial_cmp(other.as_ref())
}
}

impl Ord for BorrowedBytes<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(other.0)
self.buf.cmp(other.buf)
}
}

impl<'a> IntoIterator for BorrowedBytes<'a> {
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
type Item = &'a u8;
type IntoIter = slice::Iter<'a, u8>;

fn into_iter(self) -> Self::IntoIter {
self.0.iter()
self.iter()
}
}

impl<'a> From<&'a String> for BorrowedBytes<'a> {
#[inline]
fn from(value: &'a String) -> Self {
let (buf, _lua) = unsafe { value.to_slice() };
let borrow = Cow::Borrowed(value);
Self { buf, borrow, _lua }
}
}

Expand Down
Loading

0 comments on commit fca38a6

Please sign in to comment.