Compare commits
No commits in common. "91302db72515fb754a41fa543497ab047218e775" and "a81271c0a8a2b07cb6f2291ff7af8d1e8d399ecf" have entirely different histories.
91302db725
...
a81271c0a8
@ -1,15 +1,15 @@
|
|||||||
//! The `lb:fs` library provides utilities for interacting with the file system asynchronously.
|
//! The `lb:fs` module provides utilities for interacting with the file system asynchronously.
|
||||||
//!
|
//!
|
||||||
//! # Exports
|
//! # Exports
|
||||||
//!
|
//!
|
||||||
//! See [`lb_libfs`] for items exported by this library.
|
//! See [`lb_libfs`] for items exported by this module.
|
||||||
use luaffi::{cdef, metatype};
|
use luaffi::{cdef, metatype};
|
||||||
use std::io;
|
use std::io;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
/// Items exported by the `lb:fs` library.
|
/// Items exported by the `lb:fs` module.
|
||||||
///
|
///
|
||||||
/// This library can be obtained by calling `require` in Lua.
|
/// This module can be obtained by calling `require` in Lua.
|
||||||
///
|
///
|
||||||
/// ```lua
|
/// ```lua
|
||||||
/// local fs = require("lb:fs");
|
/// local fs = require("lb:fs");
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
//! The `lb:net` library provides an asynchronous network API for creating TCP or UDP servers and
|
//! The `lb:net` module provides an asynchronous network API for creating TCP or UDP servers and
|
||||||
//! clients.
|
//! clients.
|
||||||
//!
|
//!
|
||||||
//! # Exports
|
//! # Exports
|
||||||
//!
|
//!
|
||||||
//! See [`lb_libnet`] for items exported by this library.
|
//! See [`lb_libnet`] for items exported by this module.
|
||||||
use derive_more::{From, FromStr};
|
use derive_more::{From, FromStr};
|
||||||
use luaffi::{cdef, metatype};
|
use luaffi::{cdef, metatype};
|
||||||
use std::{
|
use std::{
|
||||||
@ -12,9 +12,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
||||||
|
|
||||||
/// Items exported by the `lb:net` library.
|
/// Items exported by the `lb:net` module.
|
||||||
///
|
///
|
||||||
/// This library can be obtained by calling `require` in Lua.
|
/// This module can be obtained by calling `require` in Lua.
|
||||||
///
|
///
|
||||||
/// ```lua
|
/// ```lua
|
||||||
/// local net = require("lb:net");
|
/// local net = require("lb:net");
|
||||||
|
|||||||
@ -41,7 +41,6 @@ impl Builder {
|
|||||||
let mut s = State::new()?;
|
let mut s = State::new()?;
|
||||||
let mut chunk = Chunk::new(self.registry.done());
|
let mut chunk = Chunk::new(self.registry.done());
|
||||||
chunk.extend(include_bytes!("./runtime.lua"));
|
chunk.extend(include_bytes!("./runtime.lua"));
|
||||||
// println!("{chunk}");
|
|
||||||
s.eval(chunk.path("[luby]"), 0, 0)?;
|
s.eval(chunk.path("[luby]"), 0, 0)?;
|
||||||
s
|
s
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,10 +7,6 @@ authors.workspace = true
|
|||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[features]
|
|
||||||
option_ref_abi = []
|
|
||||||
option_string_abi = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bstr = "1.12.0"
|
bstr = "1.12.0"
|
||||||
luaffi_impl = { path = "../luaffi_impl" }
|
luaffi_impl = { path = "../luaffi_impl" }
|
||||||
|
|||||||
@ -11,13 +11,13 @@ use std::{
|
|||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod future;
|
||||||
|
pub mod string;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[path = "./internal.rs"]
|
#[path = "./internal.rs"]
|
||||||
pub mod __internal;
|
pub mod __internal;
|
||||||
pub mod future;
|
|
||||||
pub mod option;
|
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod string;
|
|
||||||
|
|
||||||
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
||||||
// garbage-collected until the end of the function (used in string.rs when string marshalling is
|
// garbage-collected until the end of the function (used in string.rs when string marshalling is
|
||||||
@ -857,9 +857,7 @@ macro_rules! impl_ptr_intoabi {
|
|||||||
|
|
||||||
impl_ptr_intoabi!(*const T);
|
impl_ptr_intoabi!(*const T);
|
||||||
impl_ptr_intoabi!(*mut T);
|
impl_ptr_intoabi!(*mut T);
|
||||||
#[cfg(feature = "option_ref_abi")] // disabled because it conflicts with the generic Option<T> impl
|
|
||||||
impl_ptr_intoabi!(Option<&'static T>);
|
impl_ptr_intoabi!(Option<&'static T>);
|
||||||
#[cfg(feature = "option_ref_abi")]
|
|
||||||
impl_ptr_intoabi!(Option<&'static mut T>);
|
impl_ptr_intoabi!(Option<&'static mut T>);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
__internal::{disp, display},
|
|
||||||
Cdef, CdefBuilder, IntoFfi, KEEP_FN, Type, TypeBuilder, TypeType,
|
|
||||||
};
|
|
||||||
use std::{ffi::c_int, fmt::Display};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub enum lua_option<T> {
|
|
||||||
None, // __tag = 0
|
|
||||||
Some(T), // __tag = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> Type for lua_option<T> {
|
|
||||||
fn name() -> impl Display {
|
|
||||||
display!("option__{}", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
|
||||||
display!("struct {} {name}", Self::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
|
||||||
b.cdef::<Self>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> Cdef for lua_option<T> {
|
|
||||||
fn build(b: &mut CdefBuilder) {
|
|
||||||
b.field::<c_int>("__tag");
|
|
||||||
(T::ty() != TypeType::Void).then(|| b.field::<T>("__value"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: IntoFfi> IntoFfi for Option<T> {
|
|
||||||
type Into = lua_option<T::Into>;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
match self {
|
|
||||||
Some(value) => lua_option::Some(T::convert(value)),
|
|
||||||
None => lua_option::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn require_owned() -> bool {
|
|
||||||
// lua_option is only used to transmit information about whether we have a value or not and
|
|
||||||
// is forgotten immediately after use, so there is no need for an owned option
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
disp(move |f| {
|
|
||||||
write!(f, "if {ret}.__tag ~= 0 then ")?;
|
|
||||||
match T::Into::ty() {
|
|
||||||
TypeType::Void => write!(f, "{ret} = nil; ")?, // for void options, we don't have a __value
|
|
||||||
TypeType::Primitive => {
|
|
||||||
// can always copy primitives to stack
|
|
||||||
write!(f, "{ret} = {ret}.__value; {}", T::postlude(ret))?;
|
|
||||||
}
|
|
||||||
TypeType::Aggregate => {
|
|
||||||
let ct = T::Into::name();
|
|
||||||
if T::require_owned() {
|
|
||||||
// inner value requires ownership; copy it into its own cdata and forget
|
|
||||||
// option.
|
|
||||||
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
|
||||||
write!(f, "{}", T::postlude(ret))?;
|
|
||||||
} else {
|
|
||||||
// inner value is a "temporary" like an option itself and doesn't require
|
|
||||||
// full ownership of itself. we just need to keep the option object alive
|
|
||||||
// until its postlude completes.
|
|
||||||
write!(f, "local {ret}_keep = {ret}; {ret} = {ret}.__value; ")?;
|
|
||||||
write!(f, "do {}end; ", T::postlude(ret))?;
|
|
||||||
write!(f, "__C.{KEEP_FN}({ret}_keep); ")?; // keep original option alive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write!(f, "else {ret} = nil; end; ")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -72,12 +72,12 @@ unsafe impl<T: IntoFfi, E: Display> IntoFfi for Result<T, E> {
|
|||||||
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
||||||
write!(f, "{}", T::postlude(ret))?;
|
write!(f, "{}", T::postlude(ret))?;
|
||||||
} else {
|
} else {
|
||||||
// inner value is a "temporary" like a result itself and doesn't require
|
// inner value is a "temporary" itself and doesn't require full ownership of
|
||||||
// full ownership of itself. we just need to keep the result object alive
|
// itself. we just need to keep the result object alive until its postlude
|
||||||
// until its postlude completes.
|
// completes.
|
||||||
write!(f, "local {ret}_keep = {ret}; {ret} = {ret}.__value; ")?;
|
write!(f, "local __{ret} = {ret}; {ret} = {ret}.__value; ")?;
|
||||||
write!(f, "do {}end; ", T::postlude(ret))?;
|
write!(f, "do {}end; ", T::postlude(ret))?;
|
||||||
write!(f, "__C.{KEEP_FN}({ret}_keep); ")?; // keep original result alive
|
write!(f, "__C.{KEEP_FN}(__{ret}); ")?; // keep original result alive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use bstr::{BStr, BString};
|
use bstr::{BStr, BString};
|
||||||
use luaffi_impl::{cdef, metatype};
|
use luaffi_impl::{cdef, metatype};
|
||||||
use std::{fmt::Display, mem::ManuallyDrop, slice};
|
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
|
||||||
|
|
||||||
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||||
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
||||||
@ -42,7 +42,6 @@ impl lua_buf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "option_string_abi")]
|
|
||||||
pub(crate) fn null() -> Self {
|
pub(crate) fn null() -> Self {
|
||||||
Self {
|
Self {
|
||||||
__ptr: ptr::null(),
|
__ptr: ptr::null(),
|
||||||
@ -72,7 +71,6 @@ impl lua_buffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "option_string_abi")]
|
|
||||||
pub(crate) fn null() -> Self {
|
pub(crate) fn null() -> Self {
|
||||||
Self {
|
Self {
|
||||||
__ptr: ptr::null_mut(),
|
__ptr: ptr::null_mut(),
|
||||||
@ -230,64 +228,56 @@ impl_into_via!(&'static str, &'static BStr);
|
|||||||
impl_into_via!(BString, Vec<u8>);
|
impl_into_via!(BString, Vec<u8>);
|
||||||
impl_into_via!(String, BString);
|
impl_into_via!(String, BString);
|
||||||
|
|
||||||
// `Option<String>: From/IntoFfi` isn't implemented because it conflicts with the generic
|
macro_rules! impl_optional_from {
|
||||||
// `Option<T>: From/IntoFfi` impl and rust doesn't have specialisation yet (and probably not anytime
|
($ty:ty) => {
|
||||||
// soon). this is fine for now because we have specialisation for string-like parameters implemented
|
unsafe impl<'s> FromFfi for Option<$ty> {
|
||||||
// in the #[metatype] macro already, and string returns wrapped in `Option<T>` isn't much additional
|
type From = <$ty as FromFfi>::From;
|
||||||
// overhead.
|
|
||||||
#[cfg(feature = "option_string_abi")]
|
|
||||||
mod impl_option_string {
|
|
||||||
macro_rules! impl_optional_from {
|
|
||||||
($ty:ty) => {
|
|
||||||
unsafe impl<'s> FromFfi for Option<$ty> {
|
|
||||||
type From = <$ty as FromFfi>::From;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
<$ty as FromFfi>::require_keepalive()
|
<$ty as FromFfi>::require_keepalive()
|
||||||
}
|
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
|
||||||
// just pass a null pointer if argument is nil
|
|
||||||
display!(
|
|
||||||
"if {arg} ~= nil then {}end; ",
|
|
||||||
<$ty as FromFfi>::prelude(arg)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_optional_from!(&'s [u8]);
|
fn prelude(arg: &str) -> impl Display {
|
||||||
impl_optional_from!(&'s BStr);
|
// just pass a null pointer if argument is nil
|
||||||
impl_optional_from!(&'s str);
|
display!(
|
||||||
|
"if {arg} ~= nil then {}end; ",
|
||||||
macro_rules! impl_optional_into {
|
<$ty as FromFfi>::prelude(arg)
|
||||||
($ty:ty, $null:expr) => {
|
)
|
||||||
unsafe impl IntoFfi for Option<$ty> {
|
|
||||||
type Into = <$ty as IntoFfi>::Into;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
self.map_or($null, <$ty as IntoFfi>::convert)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
display!(
|
|
||||||
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
|
||||||
<$ty as IntoFfi>::postlude(ret)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_optional_into!(&'static [u8], lua_buf::null());
|
fn convert(from: Self::From) -> Self {
|
||||||
impl_optional_into!(&'static BStr, lua_buf::null());
|
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
||||||
impl_optional_into!(&'static str, lua_buf::null());
|
}
|
||||||
impl_optional_into!(Vec<u8>, lua_buffer::null());
|
}
|
||||||
impl_optional_into!(BString, lua_buffer::null());
|
};
|
||||||
impl_optional_into!(String, lua_buffer::null());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_optional_from!(&'s [u8]);
|
||||||
|
impl_optional_from!(&'s BStr);
|
||||||
|
impl_optional_from!(&'s str);
|
||||||
|
|
||||||
|
macro_rules! impl_optional_into {
|
||||||
|
($ty:ty, $null:expr) => {
|
||||||
|
unsafe impl IntoFfi for Option<$ty> {
|
||||||
|
type Into = <$ty as IntoFfi>::Into;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
|
self.map_or($null, <$ty as IntoFfi>::convert)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str) -> impl Display {
|
||||||
|
display!(
|
||||||
|
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
||||||
|
<$ty as IntoFfi>::postlude(ret)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_optional_into!(&'static [u8], lua_buf::null());
|
||||||
|
impl_optional_into!(&'static BStr, lua_buf::null());
|
||||||
|
impl_optional_into!(&'static str, lua_buf::null());
|
||||||
|
impl_optional_into!(Vec<u8>, lua_buffer::null());
|
||||||
|
impl_optional_into!(BString, lua_buffer::null());
|
||||||
|
impl_optional_into!(String, lua_buffer::null());
|
||||||
|
|||||||
28
src/main.rs
28
src/main.rs
@ -1,9 +1,7 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use std::{
|
use std::{backtrace::Backtrace, fmt::Display, net::SocketAddr, num::NonZero, panic, thread};
|
||||||
backtrace::Backtrace, fmt::Display, net::SocketAddr, num::NonZero, panic, process, thread,
|
|
||||||
};
|
|
||||||
use sysexits::ExitCode;
|
use sysexits::ExitCode;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
@ -22,23 +20,21 @@ fn panic_cb(panic: &panic::PanicHookInfo) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
eprint!(
|
eprint!(
|
||||||
"{}\n{trace}",
|
"{}:\n{trace}",
|
||||||
format_args!(
|
format_args!(
|
||||||
"thread '{}' panicked at {location}: {msg}",
|
"thread '{}' panicked at {location}: {msg}",
|
||||||
thread::current().name().unwrap_or("<unnamed>")
|
thread::current().name().unwrap_or("<unnamed>")
|
||||||
)
|
)
|
||||||
.red()
|
.red()
|
||||||
.bold()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}",
|
"{}",
|
||||||
format_args!(
|
format_args!(
|
||||||
"luby should never panic. Please kindly report this bug at {}.",
|
"This is a bug in luby. Please kindly report this at {}.",
|
||||||
env!("CARGO_PKG_REPOSITORY")
|
env!("CARGO_PKG_REPOSITORY")
|
||||||
)
|
)
|
||||||
.yellow()
|
.yellow()
|
||||||
.bold()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,17 +110,17 @@ impl Args {
|
|||||||
|
|
||||||
fn exit_err<T, E: Display>(code: ExitCode) -> impl FnOnce(E) -> T {
|
fn exit_err<T, E: Display>(code: ExitCode) -> impl FnOnce(E) -> T {
|
||||||
move |err| {
|
move |err| {
|
||||||
eprintln!("{}", err.red().bold());
|
eprintln!("{}", err.red());
|
||||||
code.exit()
|
code.exit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), ExitCode> {
|
||||||
panic::set_hook(Box::new(panic_cb));
|
panic::set_hook(Box::new(panic_cb));
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
if args.version {
|
if args.version {
|
||||||
return print_version();
|
return Ok(print_version());
|
||||||
}
|
}
|
||||||
|
|
||||||
init_logger(&args);
|
init_logger(&args);
|
||||||
@ -236,13 +232,13 @@ fn parse_jitlib_cmd(s: &str) -> Option<(&str, &str)> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn main_async(args: Args, state: &mut luajit::State) {
|
async fn main_async(args: Args, state: &mut luajit::State) -> Result<(), ExitCode> {
|
||||||
for ref path in args.path {
|
for ref path in args.path {
|
||||||
let mut s = state.guard();
|
let mut s = state.guard();
|
||||||
let chunk = match std::fs::read(path) {
|
let chunk = match std::fs::read(path) {
|
||||||
Ok(chunk) => chunk,
|
Ok(chunk) => chunk,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{}", format_args!("{path}: {err}").red().bold());
|
eprintln!("{}", format_args!("{path}: {err}").red());
|
||||||
ExitCode::NoInput.exit();
|
ExitCode::NoInput.exit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -252,11 +248,13 @@ async fn main_async(args: Args, state: &mut luajit::State) {
|
|||||||
|
|
||||||
if let Err(err) = s.call_async(0, 0).await {
|
if let Err(err) = s.call_async(0, 0).await {
|
||||||
match err.trace() {
|
match err.trace() {
|
||||||
Some(trace) => eprintln!("{}\n{trace}", err.red().bold()),
|
Some(trace) => eprintln!("{}\n{trace}", err.red()), // runtime error
|
||||||
None => eprintln!("{}", err.red().bold()),
|
None => eprintln!("{}", err.red()),
|
||||||
}
|
}
|
||||||
|
|
||||||
process::exit(1);
|
ExitCode::DataErr.exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
21
test.lua
21
test.lua
@ -1,19 +1,6 @@
|
|||||||
local fs = require("lb:fs")
|
local ffi = require("ffi")
|
||||||
|
local lb = ffi.new("struct lb_core")
|
||||||
|
|
||||||
-- do
|
print(lb)
|
||||||
-- local start = os.clock()
|
|
||||||
-- for i = 1, 50000 do
|
|
||||||
-- fs:read("crates/luaffi_impl/src/metatype.rs")
|
|
||||||
-- end
|
|
||||||
-- local finish = os.clock()
|
|
||||||
-- print("finish in " .. (finish - start))
|
|
||||||
-- end
|
|
||||||
|
|
||||||
do
|
lb.spawn("")
|
||||||
local start = os.clock()
|
|
||||||
for i = 1, 30000 do
|
|
||||||
fs:read_sync("bacon.toml")
|
|
||||||
end
|
|
||||||
local finish = os.clock()
|
|
||||||
print("finish in " .. (finish - start))
|
|
||||||
end
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user