diff --git a/crates/lune-std-ffi/src/data/box_data/mod.rs b/crates/lune-std-ffi/src/data/box_data/mod.rs index 70ce3b38..e8c4b88a 100644 --- a/crates/lune-std-ffi/src/data/box_data/mod.rs +++ b/crates/lune-std-ffi/src/data/box_data/mod.rs @@ -138,6 +138,9 @@ impl LuaUserData for BoxData { } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { method_provider::provide_copy_from(methods); + method_provider::provide_read_string(methods); + method_provider::provide_write_string(methods); + // For convenience, :zero returns box itself. methods.add_function_mut("zero", |_lua, this: LuaAnyUserData| { this.borrow_mut::()?.zero(); diff --git a/crates/lune-std-ffi/src/data/helper.rs b/crates/lune-std-ffi/src/data/helper.rs index cfd304cc..0d48e1c9 100644 --- a/crates/lune-std-ffi/src/data/helper.rs +++ b/crates/lune-std-ffi/src/data/helper.rs @@ -26,10 +26,10 @@ pub mod method_provider { let src = src.get_ffi_data()?; if !src.check_inner_boundary(src_offset, length) { - return Err(LuaError::external("Source boundary check failed")); + return Err(LuaError::external("Source out of bounds")); } if !this.check_inner_boundary(dst_offset, length) { - return Err(LuaError::external("Self boundary check failed")); + return Err(LuaError::external("Self out of bounds")); } this.copy_from(&src, length, dst_offset, src_offset); @@ -39,6 +39,61 @@ pub mod method_provider { ); } - // TODO: writeString, readString, writeBase64 and readBase64 methods + // Implement readString method + pub fn provide_read_string<'lua, Target, M>(methods: &mut M) + where + Target: FfiData, + M: LuaUserDataMethods<'lua, Target>, + { + methods.add_method( + "readString", + |lua, this, (length, offset): (usize, Option)| unsafe { + let offset = offset.unwrap_or(0); + + if !this.check_inner_boundary(offset, length) { + return Err(LuaError::external("Source out of bounds")); + } + + lua.create_string(this.read_string(length, offset)) + }, + ); + } + + // Implement writeString method + pub fn provide_write_string<'lua, Target, M>(methods: &mut M) + where + Target: FfiData, + M: LuaUserDataMethods<'lua, Target>, + { + methods.add_method( + "writeString", + |_lua, + this, + (string, length, dst_offset, src_offset): ( + LuaString, + usize, + Option, + Option, + )| unsafe { + let dst_offset = dst_offset.unwrap_or(0); + let src_offset = src_offset.unwrap_or(0); + + // Source string boundary check + if string.as_bytes().len() < src_offset + length { + return Err(LuaError::external("Source out of bounds")); + } + + // Self boundary check + if !this.check_inner_boundary(dst_offset, length) { + return Err(LuaError::external("Self out of bounds")); + } + + this.write_string(string, length, dst_offset, src_offset); + Ok(()) + }, + ); + } + // TODO: Bit operation support + // TODO: writeBase64 and readBase64 methods } diff --git a/crates/lune-std-ffi/src/data/ref_data/mod.rs b/crates/lune-std-ffi/src/data/ref_data/mod.rs index 55090155..42701b30 100644 --- a/crates/lune-std-ffi/src/data/ref_data/mod.rs +++ b/crates/lune-std-ffi/src/data/ref_data/mod.rs @@ -165,6 +165,8 @@ impl FfiData for RefData { impl LuaUserData for RefData { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { method_provider::provide_copy_from(methods); + method_provider::provide_read_string(methods); + method_provider::provide_write_string(methods); methods.add_method("deref", |_lua, this, ()| unsafe { this.dereference() }); methods.add_function("offset", |lua, (this, offset): (LuaAnyUserData, isize)| { diff --git a/crates/lune-std-ffi/src/ffi/mod.rs b/crates/lune-std-ffi/src/ffi/mod.rs index 5131b968..dc928305 100644 --- a/crates/lune-std-ffi/src/ffi/mod.rs +++ b/crates/lune-std-ffi/src/ffi/mod.rs @@ -71,8 +71,33 @@ pub trait FfiData { src_offset: isize, ) { self.get_inner_pointer() + .cast::() .byte_offset(dst_offset) - .copy_from(src.get_inner_pointer().byte_offset(src_offset), length); + .copy_from( + src.get_inner_pointer().cast::().byte_offset(src_offset), + length, + ); + } + unsafe fn read_string(&self, length: usize, offset: isize) -> Vec { + let mut string = Vec::::with_capacity(length); + string.as_mut_ptr().copy_from( + self.get_inner_pointer().cast::().byte_offset(offset), + length, + ); + string.set_len(length); + string + } + unsafe fn write_string( + &self, + src: LuaString, + length: usize, + dst_offset: isize, + src_offset: usize, + ) { + self.get_inner_pointer() + .cast::() + .byte_offset(dst_offset) + .copy_from(src.to_pointer().cast::().byte_add(src_offset), length); } } diff --git a/types/ffi.luau b/types/ffi.luau index d17d053d..d0dbd75b 100644 --- a/types/ffi.luau +++ b/types/ffi.luau @@ -125,9 +125,9 @@ export type RefData = { Copy content from another data with specific length. @param src The source data - @param len The amount of data to copy, in bytes - @param dstOffset The offset in the destination where the data will be pasted - @param srcOffset The offset in the source data from where the data will be copied + @param length The amount of data to copy, in bytes + @param dstOffset The offset in the destination where the content will be pasted + @param srcOffset The offset in the source data from where the content will be copied ]=] copyFrom: ( self: RefData, @@ -136,6 +136,37 @@ export type RefData = { dstOffset: number, srcOffset: number ) -> (), + --[=[ + @within RefData + @tag Method + @method readString + + Read string from data with specific length. + + @param length The amount of data to read, in bytes + @param offset Offset to read string from + @return A string + ]=] + readString: (self: RefData, length: number, offset: number?) -> string, + --[=[ + @within RefData + @tag Method + @method writeString + + Write string into data. + + @param src The source string + @param length The amount of data to write, in bytes + @param dstOffset The offset in the destination where the content will be pasted + @param srcOffset The offset in the source string from where the content will be copied + ]=] + writeString: ( + self: RefData, + src: string, + length: number, + dstOffset: number, + srcOffset: number + ) -> (), } --[=[ @@ -197,9 +228,9 @@ export type BoxData = { Copy content from another data with specific length. @param src The source data - @param len The amount of data to copy, in bytes - @param dstOffset The offset in the destination where the data will be pasted - @param srcOffset The offset in the source data from where the data will be copied + @param length The amount of data to copy, in bytes + @param dstOffset The offset in the destination where the content will be pasted + @param srcOffset The offset in the source data from where the content will be copied ]=] copyFrom: ( self: BoxData, @@ -208,6 +239,37 @@ export type BoxData = { dstOffset: number, srcOffset: number ) -> (), + --[=[ + @within BoxData + @tag Method + @method readString + + Read string from data with specific length. + + @param length The amount of data to read, in bytes + @param offset Offset to read string from + @return A string + ]=] + readString: (self: BoxData, length: number, offset: number?) -> string, + --[=[ + @within BoxData + @tag Method + @method writeString + + Write string into data. + + @param src The source string + @param length The amount of data to write, in bytes + @param dstOffset The offset in the destination where the content will be pasted + @param srcOffset The offset in the source string from where the content will be copied + ]=] + writeString: ( + self: BoxData, + src: string, + length: number, + dstOffset: number, + srcOffset: number + ) -> (), } --[=[ @@ -305,10 +367,10 @@ export type CTypeInfo = { Create an array subtype with specific length. - @param len The length of the array + @param length The length of the array @return An array subtype ]=] - arr: (self: CTypeInfo, len: number) -> CArrInfo, R>, + arr: (self: CTypeInfo, length: number) -> CArrInfo, R>, -- Create/Read/Write/Copy --[=[ @@ -353,10 +415,10 @@ export type CTypeInfo = { Copy values ​​from the source and paste them into the target. - @param dst Where the data will be pasted + @param dst Where the content will be pasted @param src The source data - @param dstOffset The offset in the destination where the data will be pasted - @param srcOffset The offset in the source data from where the data will be copied + @param dstOffset The offset in the destination where the content will be pasted + @param srcOffset The offset in the source data from where the content will be copied ]=] copyData: ( self: CTypeInfo, @@ -434,10 +496,10 @@ export type CPtrInfo = { Create an array subtype with specific length. - @param len The length of the array + @param length The length of the array @return An array subtype ]=] - arr: (self: CPtrInfo, len: number) -> any, + arr: (self: CPtrInfo, length: number) -> any, -- FIXME: recursive types; result 'any' should be CPtrInfo> --[=[ @within CPtrInfo @@ -585,10 +647,10 @@ export type CArrInfo = { Copy values ​​from the source and paste them into the target. - @param dst Where the data will be pasted + @param dst Where the content will be pasted @param src The source data - @param dstOffset The offset in the dst where the data will be pasted - @param srcOffset The offset in the source data from where the data will be copied + @param dstOffset The offset in the dst where the content will be pasted + @param srcOffset The offset in the source data from where the content will be copied ]=] copyData: ( self: CArrInfo, @@ -677,10 +739,10 @@ export type CStructInfo = { Create a struct array type. - @param len The length of the array + @param length The length of the array @return A struct array type ]=] - arr: (self: CStructInfo, len: number) -> CArrInfo, + arr: (self: CStructInfo, length: number) -> CArrInfo, --[=[ @within CSturctInfo @tag Method @@ -740,10 +802,10 @@ export type CStructInfo = { Copy values from the source and paste them into the target. - @param dst Where the data will be pasted + @param dst Where the content will be pasted @param src The source data - @param dstOffset The offset in the destination where the data will be pasted - @param srcOffset The offset in the source data from where the data will be copied + @param dstOffset The offset in the destination where the content will be pasted + @param srcOffset The offset in the source data from where the content will be copied ]=] copyData: ( self: CStructInfo,