Skip to content

Commit c481c87

Browse files
committed
Add Lua::create_buffer_with_capacity method
This allow creating a preallocated buffer with specified size initialized to zero.
1 parent 85b280a commit c481c87

File tree

4 files changed

+48
-20
lines changed

4 files changed

+48
-20
lines changed

src/state.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,7 @@ impl Lua {
11501150
}
11511151
}
11521152

1153-
/// Create and return an interned Lua string.
1153+
/// Creates and returns an interned Lua string.
11541154
///
11551155
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
11561156
/// and `&String`, you can also pass plain `&[u8]` here.
@@ -1159,27 +1159,32 @@ impl Lua {
11591159
unsafe { self.lock().create_string(s) }
11601160
}
11611161

1162-
/// Create and return a Luau [buffer] object from a byte slice of data.
1162+
/// Creates and returns a Luau [buffer] object from a byte slice of data.
11631163
///
11641164
/// [buffer]: https://luau.org/library#buffer-library
11651165
#[cfg(any(feature = "luau", doc))]
11661166
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
1167-
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
1167+
pub fn create_buffer(&self, data: impl AsRef<[u8]>) -> Result<Buffer> {
11681168
let lua = self.lock();
1169-
let state = lua.state();
1169+
let data = data.as_ref();
11701170
unsafe {
1171-
if lua.unlikely_memory_error() {
1172-
crate::util::push_buffer(state, buf.as_ref(), false)?;
1173-
return Ok(Buffer(lua.pop_ref()));
1174-
}
1175-
1176-
let _sg = StackGuard::new(state);
1177-
check_stack(state, 3)?;
1178-
crate::util::push_buffer(state, buf.as_ref(), true)?;
1179-
Ok(Buffer(lua.pop_ref()))
1171+
let (ptr, buffer) = lua.create_buffer_with_capacity(data.len())?;
1172+
ptr.copy_from_nonoverlapping(data.as_ptr(), data.len());
1173+
Ok(buffer)
11801174
}
11811175
}
11821176

1177+
/// Creates and returns a Luau [buffer] object with the specified size.
1178+
///
1179+
/// Size limit is 1GB. All bytes will be initialized to zero.
1180+
///
1181+
/// [buffer]: https://luau.org/library#buffer-library
1182+
#[cfg(any(feature = "luau", doc))]
1183+
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
1184+
pub fn create_buffer_with_capacity(&self, size: usize) -> Result<Buffer> {
1185+
unsafe { Ok(self.lock().create_buffer_with_capacity(size)?.1) }
1186+
}
1187+
11831188
/// Creates and returns a new empty table.
11841189
#[inline]
11851190
pub fn create_table(&self) -> Result<Table> {

src/state/raw.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,20 @@ impl RawLua {
523523
Ok(String(self.pop_ref()))
524524
}
525525

526+
#[cfg(feature = "luau")]
527+
pub(crate) unsafe fn create_buffer_with_capacity(&self, size: usize) -> Result<(*mut u8, crate::Buffer)> {
528+
let state = self.state();
529+
if self.unlikely_memory_error() {
530+
let ptr = crate::util::push_buffer(state, size, false)?;
531+
return Ok((ptr, crate::Buffer(self.pop_ref())));
532+
}
533+
534+
let _sg = StackGuard::new(state);
535+
check_stack(state, 3)?;
536+
let ptr = crate::util::push_buffer(state, size, true)?;
537+
Ok((ptr, crate::Buffer(self.pop_ref())))
538+
}
539+
526540
/// See [`Lua::create_table_with_capacity`]
527541
pub(crate) unsafe fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
528542
let state = self.state();

src/util/mod.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,13 @@ pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect:
101101
// Uses 3 stack spaces (when protect), does not call checkstack.
102102
#[cfg(feature = "luau")]
103103
#[inline(always)]
104-
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
105-
let data = if protect {
106-
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
104+
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, size: usize, protect: bool) -> Result<*mut u8> {
105+
let data = if protect || size > const { 1024 * 1024 * 1024 } {
106+
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, size))?
107107
} else {
108-
ffi::lua_newbuffer(state, b.len())
108+
ffi::lua_newbuffer(state, size)
109109
};
110-
let buf = slice::from_raw_parts_mut(data as *mut u8, b.len());
111-
buf.copy_from_slice(b);
112-
Ok(())
110+
Ok(data as *mut u8)
113111
}
114112

115113
// Uses 3 stack spaces, does not call checkstack.

tests/buffer.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,14 @@ fn test_buffer_out_of_bounds_write() {
5555
let buf = lua.create_buffer(b"hello, world!").unwrap();
5656
buf.write_bytes(14, b"!!");
5757
}
58+
59+
#[test]
60+
fn create_large_buffer() {
61+
let lua = Lua::new();
62+
let err = lua.create_buffer_with_capacity(1_073_741_824 + 1).unwrap_err(); // 1GB
63+
assert!(err.to_string().contains("memory allocation error"));
64+
65+
// Normal buffer is okay
66+
let buf = lua.create_buffer_with_capacity(1024 * 1024).unwrap();
67+
assert_eq!(buf.len(), 1024 * 1024);
68+
}

0 commit comments

Comments
 (0)