diff --git a/core/src/demo/xmake.lua b/core/src/demo/xmake.lua index edd6adfddc0..d0ac789098d 100644 --- a/core/src/demo/xmake.lua +++ b/core/src/demo/xmake.lua @@ -7,9 +7,6 @@ target("demo") -- make as a binary set_kind("binary") - -- set basename of target file - set_basename("xmake") - -- add defines add_defines("__tb_prefix__=\"xmake\"") @@ -30,6 +27,7 @@ target("demo") -- add links if is_plat("windows") then add_links("ws2_32", "advapi32", "shell32") + add_ldflags("/export:malloc", "/export:free") elseif is_plat("android") then add_links("m", "c") elseif is_plat("macosx") then @@ -49,6 +47,6 @@ target("demo") -- copy target to the build directory after_build(function (target) - os.cp(target:targetfile(), "$(buildir)") + os.cp(target:targetfile(), "$(buildir)/xmake" .. (is_plat("windows") and ".exe" or "")) end) diff --git a/core/src/xmake/io/file_close.c b/core/src/xmake/io/file_close.c index 6b3efdd988f..1e3ebd13196 100644 --- a/core/src/xmake/io/file_close.c +++ b/core/src/xmake/io/file_close.c @@ -42,7 +42,7 @@ tb_int_t xm_io_file_close(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "close(invalid file)!"); + xm_io_return_error(lua, "close(invalid file)!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); @@ -88,6 +88,6 @@ tb_int_t xm_io_file_close(lua_State* lua) lua_pushboolean(lua, tb_true); return 1; } - else xm_io_file_return_error(lua, "cannot close this file!"); + else xm_io_return_error(lua, "cannot close this file!"); } diff --git a/core/src/xmake/io/file_flush.c b/core/src/xmake/io/file_flush.c index 72969b09d4b..a4bf0f672de 100644 --- a/core/src/xmake/io/file_flush.c +++ b/core/src/xmake/io/file_flush.c @@ -69,7 +69,7 @@ tb_int_t xm_io_file_flush(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "flush(invalid file)!"); + xm_io_return_error(lua, "flush(invalid file)!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); @@ -82,5 +82,5 @@ tb_int_t xm_io_file_flush(lua_State* lua) lua_pushboolean(lua, tb_true); return 1; } - else xm_io_file_return_error(lua, "failed to flush file"); + else xm_io_return_error(lua, "failed to flush file"); } diff --git a/core/src/xmake/io/file_isatty.c b/core/src/xmake/io/file_isatty.c index d8c5183d87c..06eea4b2998 100644 --- a/core/src/xmake/io/file_isatty.c +++ b/core/src/xmake/io/file_isatty.c @@ -42,7 +42,7 @@ tb_int_t xm_io_file_isatty(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "isatty(invalid file)!"); + xm_io_return_error(lua, "isatty(invalid file)!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); diff --git a/core/src/xmake/io/file_open.c b/core/src/xmake/io/file_open.c index 46e63cd62aa..9db81a82cfa 100644 --- a/core/src/xmake/io/file_open.c +++ b/core/src/xmake/io/file_open.c @@ -217,10 +217,10 @@ tb_int_t xm_io_file_open(lua_State* lua) else { if (stream) tb_stream_exit(stream); - xm_io_file_return_error(lua, "file not found!"); + xm_io_return_error(lua, "file not found!"); } } - else xm_io_file_return_error(lua, "invalid open mode!"); + else xm_io_return_error(lua, "invalid open mode!"); tb_assert_and_check_return_val(encoding != XM_IO_FILE_ENCODING_UNKNOWN, 0); // open file @@ -271,9 +271,7 @@ tb_int_t xm_io_file_open(lua_State* lua) fstream = tb_null; // return errors - lua_pushnil(lua); - lua_pushliteral(lua, "failed to open file."); - return 2; + xm_io_return_error(lua, "failed to open file!"); } // make file diff --git a/core/src/xmake/io/file_rawfd.c b/core/src/xmake/io/file_rawfd.c index dcc69eceb74..a298c29c21d 100644 --- a/core/src/xmake/io/file_rawfd.c +++ b/core/src/xmake/io/file_rawfd.c @@ -58,7 +58,7 @@ tb_int_t xm_io_file_rawfd(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "get rawfd for invalid file!"); + xm_io_return_error(lua, "get rawfd for invalid file!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); @@ -76,5 +76,5 @@ tb_int_t xm_io_file_rawfd(lua_State* lua) } // get rawfd failed - xm_io_file_return_error(lua, "get rawfd for invalid file!"); + xm_io_return_error(lua, "get rawfd for invalid file!"); } diff --git a/core/src/xmake/io/file_read.c b/core/src/xmake/io/file_read.c index 438dc022d06..d5748ffe36f 100644 --- a/core/src/xmake/io/file_read.c +++ b/core/src/xmake/io/file_read.c @@ -182,7 +182,7 @@ static tb_int_t xm_io_file_read_all_directly(lua_State* lua, xm_io_file_t* file) // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, "init buffer failed!"); + xm_io_return_error(lua, "init buffer failed!"); // read all tb_byte_t data[TB_STREAM_BLOCK_MAXN]; @@ -219,7 +219,7 @@ static tb_int_t xm_io_file_read_all(lua_State* lua, xm_io_file_t* file, tb_char_ // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, "init buffer failed!"); + xm_io_return_error(lua, "init buffer failed!"); // read all tb_bool_t has_content = tb_false; @@ -239,7 +239,7 @@ static tb_int_t xm_io_file_read_all(lua_State* lua, xm_io_file_t* file, tb_char_ case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, "failed to read all"); + xm_io_return_error(lua, "failed to read all"); break; } } @@ -253,7 +253,7 @@ static tb_int_t xm_io_file_read_line(lua_State* lua, xm_io_file_t* file, tb_char // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, "init buffer failed!"); + xm_io_return_error(lua, "init buffer failed!"); // read line tb_bool_t has_content = tb_false; @@ -276,7 +276,7 @@ static tb_int_t xm_io_file_read_line(lua_State* lua, xm_io_file_t* file, tb_char case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, "failed to readline"); + xm_io_return_error(lua, "failed to readline"); break; } } @@ -289,11 +289,11 @@ static tb_int_t xm_io_file_read_n(lua_State* lua, xm_io_file_t* file, tb_char_t // check continuation if (*continuation != '\0') - xm_io_file_return_error(lua, "continuation is not supported for read number of bytes"); + xm_io_return_error(lua, "continuation is not supported for read number of bytes"); // check encoding if (file->encoding != XM_IO_FILE_ENCODING_BINARY) - xm_io_file_return_error(lua, "read number of bytes only allows binary file, reopen with 'rb' and try again"); + xm_io_return_error(lua, "read number of bytes only allows binary file, reopen with 'rb' and try again"); tb_bool_t ok = tb_false; if (n == 0) @@ -381,7 +381,7 @@ static tb_int_t xm_io_file_std_read_line(lua_State* lua, xm_io_file_t* file, tb_ // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, "init buffer failed!"); + xm_io_return_error(lua, "init buffer failed!"); // read line tb_bool_t has_content = tb_false; @@ -404,7 +404,7 @@ static tb_int_t xm_io_file_std_read_line(lua_State* lua, xm_io_file_t* file, tb_ case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, "failed to readline"); + xm_io_return_error(lua, "failed to readline"); break; } } @@ -418,7 +418,7 @@ static tb_int_t xm_io_file_std_read_all(lua_State* lua, xm_io_file_t* file, tb_c // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, "init buffer failed!"); + xm_io_return_error(lua, "init buffer failed!"); // read all tb_bool_t has_content = tb_false; @@ -438,7 +438,7 @@ static tb_int_t xm_io_file_std_read_all(lua_State* lua, xm_io_file_t* file, tb_c case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, "failed to readline"); + xm_io_return_error(lua, "failed to readline"); break; } } @@ -451,7 +451,7 @@ static tb_int_t xm_io_file_std_read_n(lua_State* lua, xm_io_file_t* file, tb_cha // check continuation if (*continuation != '\0') - xm_io_file_return_error(lua, "continuation is not supported for std streams"); + xm_io_return_error(lua, "continuation is not supported for std streams"); // io.read(0) if (n == 0) @@ -482,7 +482,7 @@ static tb_int_t xm_io_file_std_read_num(lua_State* lua, xm_io_file_t* file, tb_c // check continuation if (*continuation != '\0') - xm_io_file_return_error(lua, "continuation is not supported for std streams"); + xm_io_return_error(lua, "continuation is not supported for std streams"); // read number tb_char_t strbuf[512]; @@ -506,7 +506,7 @@ tb_int_t xm_io_file_read(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "read(invalid file)!"); + xm_io_return_error(lua, "read(invalid file)!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); @@ -521,7 +521,7 @@ tb_int_t xm_io_file_read(lua_State* lua) if (lua_isnumber(lua, 2)) { count = (tb_long_t)lua_tointeger(lua, 2); - if (count < 0) xm_io_file_return_error(lua, "invalid read size, must be positive nubmber or 0"); + if (count < 0) xm_io_return_error(lua, "invalid read size, must be positive nubmber or 0"); } else if (*mode == '*') mode++; @@ -533,10 +533,10 @@ tb_int_t xm_io_file_read(lua_State* lua) { case 'a': return xm_io_file_read_all(lua, file, continuation); case 'L': return xm_io_file_read_line(lua, file, continuation, tb_true); - case 'n': xm_io_file_return_error(lua, "read number is not implemented"); + case 'n': xm_io_return_error(lua, "read number is not implemented"); case 'l': return xm_io_file_read_line(lua, file, continuation, tb_false); default: - xm_io_file_return_error(lua, "unknonwn read mode"); + xm_io_return_error(lua, "unknonwn read mode"); return 0; } } @@ -550,7 +550,7 @@ tb_int_t xm_io_file_read(lua_State* lua) case 'n': return xm_io_file_std_read_num(lua, file, continuation); case 'l': return xm_io_file_std_read_line(lua, file, continuation, tb_false); default: - xm_io_file_return_error(lua, "unknonwn read mode"); + xm_io_return_error(lua, "unknonwn read mode"); return 0; } } diff --git a/core/src/xmake/io/file_seek.c b/core/src/xmake/io/file_seek.c index a4e14bffa90..67cfe061c75 100644 --- a/core/src/xmake/io/file_seek.c +++ b/core/src/xmake/io/file_seek.c @@ -42,7 +42,7 @@ tb_int_t xm_io_file_seek(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "seek(invalid file)!"); + xm_io_return_error(lua, "seek(invalid file)!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); @@ -66,7 +66,7 @@ tb_int_t xm_io_file_seek(lua_State* lua) tb_hong_t size = tb_stream_size(file->file_ref); if (size > 0 && size + offset <= size) offset = size + offset; - else xm_io_file_return_error(lua, "seek failed, invalid offset!"); + else xm_io_return_error(lua, "seek failed, invalid offset!"); } break; default: // "cur" @@ -79,7 +79,7 @@ tb_int_t xm_io_file_seek(lua_State* lua) lua_pushnumber(lua, (lua_Number)offset); return 1; } - else xm_io_file_return_error(lua, "seek failed!"); + else xm_io_return_error(lua, "seek failed!"); } - else xm_io_file_return_error(lua, "seek is not supported on this file"); + else xm_io_return_error(lua, "seek is not supported on this file"); } diff --git a/core/src/xmake/io/file_size.c b/core/src/xmake/io/file_size.c index 4e502bbfbb7..637268ced7d 100644 --- a/core/src/xmake/io/file_size.c +++ b/core/src/xmake/io/file_size.c @@ -42,7 +42,7 @@ tb_int_t xm_io_file_size(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "get size for invalid file!"); + xm_io_return_error(lua, "get size for invalid file!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); @@ -56,5 +56,5 @@ tb_int_t xm_io_file_size(lua_State* lua) lua_pushnumber(lua, (lua_Number)tb_stream_size(file->stream)); return 1; } - else xm_io_file_return_error(lua, "get size for invalid file!"); + else xm_io_return_error(lua, "get size for invalid file!"); } diff --git a/core/src/xmake/io/file_write.c b/core/src/xmake/io/file_write.c index 43aaf606ef1..b276a0e10ae 100644 --- a/core/src/xmake/io/file_write.c +++ b/core/src/xmake/io/file_write.c @@ -116,7 +116,7 @@ tb_int_t xm_io_file_write(lua_State* lua) // is user data? if (!lua_isuserdata(lua, 1)) - xm_io_file_return_error(lua, "write(invalid file)!"); + xm_io_return_error(lua, "write(invalid file)!"); // get file xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); diff --git a/core/src/xmake/io/prefix.h b/core/src/xmake/io/prefix.h index ba567f6fbcc..c9657039665 100644 --- a/core/src/xmake/io/prefix.h +++ b/core/src/xmake/io/prefix.h @@ -33,8 +33,8 @@ #define xm_io_file_is_std(file) ((file)->type != XM_IO_FILE_TYPE_FILE) #define xm_io_file_is_tty(file) (!!((file)->type & XM_IO_FILE_FLAG_TTY)) -// return file error -#define xm_io_file_return_error(lua, error) \ +// return io error +#define xm_io_return_error(lua, error) \ do \ { \ lua_pushnil(lua); \ diff --git a/core/src/xmake/io/socket_accept.c b/core/src/xmake/io/socket_accept.c new file mode 100644 index 00000000000..efd667c5a71 --- /dev/null +++ b/core/src/xmake/io/socket_accept.c @@ -0,0 +1,57 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_accept.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_accept" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// local sock = io.socket_accept(sock) +tb_int_t xm_io_socket_accept(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // accept socket + tb_socket_ref_t client = tb_socket_accept(sock, tb_null); + if (client) lua_pushlightuserdata(lua, (tb_pointer_t)client); + else lua_pushnil(lua); + return 1; +} + diff --git a/core/src/xmake/io/socket_bind.c b/core/src/xmake/io/socket_bind.c new file mode 100644 index 00000000000..f4c1c06b94f --- /dev/null +++ b/core/src/xmake/io/socket_bind.c @@ -0,0 +1,73 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_bind.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_bind" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.socket_bind(sock, addr, port, family) +tb_int_t xm_io_socket_bind(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushboolean(lua, tb_false); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get address + tb_char_t const* address = lua_tostring(lua, 2); + tb_assert_and_check_return_val(address, 0); + + // get port + tb_uint16_t port = (tb_uint16_t)luaL_checknumber(lua, 3); + + // get family + tb_uint8_t family = (tb_uint8_t)luaL_checknumber(lua, 4); + + // init address + tb_ipaddr_t addr; + tb_ipaddr_set(&addr, address, port, family); + + // bind socket + lua_pushboolean(lua, tb_socket_bind(sock, &addr)); + return 1; +} + diff --git a/core/src/xmake/io/socket_close.c b/core/src/xmake/io/socket_close.c new file mode 100644 index 00000000000..fe0d24f3276 --- /dev/null +++ b/core/src/xmake/io/socket_close.c @@ -0,0 +1,57 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_close.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_close" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.socket_close(sock) +tb_int_t xm_io_socket_close(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // exit socket + lua_pushboolean(lua, tb_socket_exit(sock)); + + // ok + return 1; +} + diff --git a/core/src/xmake/io/socket_connect.c b/core/src/xmake/io/socket_connect.c new file mode 100644 index 00000000000..d758a973490 --- /dev/null +++ b/core/src/xmake/io/socket_connect.c @@ -0,0 +1,73 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_connect.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_connect" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.socket_connect(sock, addr, port, family) +tb_int_t xm_io_socket_connect(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get address + tb_char_t const* address = lua_tostring(lua, 2); + tb_assert_and_check_return_val(address, 0); + + // get port + tb_uint16_t port = (tb_uint16_t)luaL_checknumber(lua, 3); + + // get family + tb_uint8_t family = (tb_uint8_t)luaL_checknumber(lua, 4); + + // init address + tb_ipaddr_t addr; + tb_ipaddr_set(&addr, address, port, family); + + // connect socket + lua_pushnumber(lua, (tb_int_t)tb_socket_connect(sock, &addr)); + return 1; +} + diff --git a/core/src/xmake/io/socket_listen.c b/core/src/xmake/io/socket_listen.c new file mode 100644 index 00000000000..c075bcb67e0 --- /dev/null +++ b/core/src/xmake/io/socket_listen.c @@ -0,0 +1,58 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_listen.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_listen" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.socket_listen(sock, backlog) +tb_int_t xm_io_socket_listen(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get backlog + tb_size_t backlog = (tb_size_t)luaL_checknumber(lua, 2); + + // listen socket + lua_pushnumber(lua, (tb_int_t)tb_socket_listen(sock, backlog)); + return 1; +} + diff --git a/core/src/xmake/io/socket_open.c b/core/src/xmake/io/socket_open.c new file mode 100644 index 00000000000..fbfd7a3c722 --- /dev/null +++ b/core/src/xmake/io/socket_open.c @@ -0,0 +1,70 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_open.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_open" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* + * io.socket_open(socktype, family) + */ +tb_int_t xm_io_socket_open(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // get socket type + tb_size_t socktype = (tb_size_t)luaL_checknumber(lua, 1); + + // get address family + tb_size_t family = (tb_size_t)luaL_checknumber(lua, 2); + + // map socket type + switch (socktype) + { + case 2: + socktype = TB_SOCKET_TYPE_UDP; + break; + case 3: + socktype = TB_SOCKET_TYPE_ICMP; + break; + default: + socktype = TB_SOCKET_TYPE_TCP; + break; + } + + // init socket + tb_socket_ref_t sock = tb_socket_init(socktype, family); + if (sock) lua_pushlightuserdata(lua, (tb_pointer_t)sock); + else lua_pushnil(lua); + return 1; +} diff --git a/core/src/xmake/io/socket_rawfd.c b/core/src/xmake/io/socket_rawfd.c new file mode 100644 index 00000000000..8682e99fca5 --- /dev/null +++ b/core/src/xmake/io/socket_rawfd.c @@ -0,0 +1,62 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this sock except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @sock socket_rawfd.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_rawfd" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ + +// socket to fd +#define xm_io_sock2fd(sock) (lua_Number)tb_sock2fd(sock) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* io.socket_rawfd(sock) + */ +tb_int_t xm_io_socket_rawfd(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + xm_io_return_error(lua, "get rawfd for invalid sock!"); + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // return result + lua_pushnumber(lua, xm_io_sock2fd(sock)); + return 1; +} diff --git a/core/src/xmake/io/socket_recv.c b/core/src/xmake/io/socket_recv.c new file mode 100644 index 00000000000..4ed41e08028 --- /dev/null +++ b/core/src/xmake/io/socket_recv.c @@ -0,0 +1,77 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_recv.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_recv" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// real, data_or_errors = io.socket_recv(sock, size) +tb_int_t xm_io_socket_recv(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get data and size + tb_byte_t data[8192]; + tb_long_t size = 0; + if (lua_isnumber(lua, 2)) size = (tb_long_t)lua_tonumber(lua, 2); + if (size < 0) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid size(%ld)!", size); + return 2; + } + if (size > sizeof(data)) + size = sizeof(data); + + // recv data + tb_long_t real = tb_socket_recv(sock, data, size); + lua_pushnumber(lua, (tb_int_t)real); + if (real > 0) + { + lua_pushlstring(lua, (tb_char_t const*)data, real); + return 2; + } + return 1; +} diff --git a/core/src/xmake/io/socket_recvfrom.c b/core/src/xmake/io/socket_recvfrom.c new file mode 100644 index 00000000000..2933a44f09c --- /dev/null +++ b/core/src/xmake/io/socket_recvfrom.c @@ -0,0 +1,101 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_recvfrom.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_recvfrom" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// real, data_or_errors, addr, port = io.socket_recvfrom(sock, size) +tb_int_t xm_io_socket_recvfrom(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get data and size + tb_byte_t buffer[8192]; + tb_byte_t* data = buffer; + tb_long_t size = 0; + if (lua_isnumber(lua, 2)) size = (tb_long_t)lua_tonumber(lua, 2); + if (size < 0) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid size(%ld)!", size); + return 2; + } + if (!size) size = sizeof(data); + else if (size > sizeof(data)) + { + data = tb_malloc_bytes(size); + if (!data) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "malloc(%ld) failed!", size); + return 2; + } + } + + // recv data + tb_ipaddr_t ipaddr; + tb_ipaddr_clear(&ipaddr); + tb_int_t retn = 1; + tb_long_t real = tb_socket_urecv(sock, &ipaddr, data, size); + lua_pushnumber(lua, (tb_int_t)real); + if (real > 0) + { + retn = 2; + lua_pushlstring(lua, (tb_char_t const*)data, real); + if (!tb_ipaddr_is_empty(&ipaddr)) + { + tb_char_t const* ipstr = tb_ipaddr_ip_cstr(&ipaddr, (tb_char_t*)buffer, sizeof(buffer)); + if (ipstr) + { + lua_pushstring(lua, ipstr); + lua_pushnumber(lua, (tb_int_t)tb_ipaddr_port(&ipaddr)); + retn = 4; + } + } + } + if (data != buffer) tb_free(data); + return retn; +} diff --git a/core/src/xmake/io/socket_send.c b/core/src/xmake/io/socket_send.c new file mode 100644 index 00000000000..8cce64f29b6 --- /dev/null +++ b/core/src/xmake/io/socket_send.c @@ -0,0 +1,109 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_send.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_send" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// io.socket_send(sock, data, start, last) +tb_int_t xm_io_socket_send(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get data + tb_size_t size = 0; + tb_byte_t const* data = tb_null; + if (lua_istable(lua, 2)) + { + // get data address + lua_pushstring(lua, "data"); + lua_gettable(lua, 2); + data = (tb_byte_t const*)(tb_size_t)(tb_long_t)lua_tonumber(lua, -1); + lua_pop(lua, 1); + + // get data size + lua_pushstring(lua, "size"); + lua_gettable(lua, 2); + size = (tb_size_t)lua_tonumber(lua, -1); + lua_pop(lua, 1); + } + else + { + size_t datasize = 0; + data = (tb_byte_t const*)luaL_checklstring(lua, 2, &datasize); + size = (tb_size_t)datasize; + } + if (!data || !size) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid data(%p) and size(%zu)!", data, size); + return 2; + } + + // get start + tb_long_t start = 1; + if (lua_isnumber(lua, 3)) start = (tb_long_t)lua_tonumber(lua, 3); + if (start < 1 || start > size) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid start position(%ld)!", start); + return 2; + } + + // get last + tb_long_t last = (tb_long_t)size; + if (lua_isnumber(lua, 4)) last = (tb_long_t)lua_tonumber(lua, 4); + if (last < start - 1 || last > size + start - 1) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid last position(%ld)!", last); + return 2; + } + + // send data + tb_long_t real = tb_socket_send(sock, data + start - 1, last - start + 1); + lua_pushnumber(lua, (tb_int_t)real); + return 1; +} diff --git a/core/src/xmake/io/socket_sendfile.c b/core/src/xmake/io/socket_sendfile.c new file mode 100644 index 00000000000..df29afd322d --- /dev/null +++ b/core/src/xmake/io/socket_sendfile.c @@ -0,0 +1,117 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_sendfile.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_sendfile" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// io.socket_sendfile(sock, file, start, last) +tb_int_t xm_io_socket_sendfile(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // check file + if (!lua_isuserdata(lua, 2)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid file!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 2); + tb_check_return_val(file, 0); + + // does not support stdfile + if (!xm_io_file_is_file(file) || !file->stream) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid file type!"); + return 2; + } + + // get file reference + tb_file_ref_t rawfile = tb_null; + if (!tb_stream_ctrl(file->stream, TB_STREAM_CTRL_FILE_GET_FILE, &rawfile) || !rawfile) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "cannot get file reference!"); + return 2; + } + + // get file size + tb_hize_t filesize = tb_file_size(rawfile); + if (!filesize) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "cannot send empty file!"); + return 2; + } + + // get start + tb_long_t start = 1; + if (lua_isnumber(lua, 3)) start = (tb_long_t)lua_tonumber(lua, 3); + if (start < 1 || start > filesize) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid start position(%ld)!", start); + return 2; + } + + // get last + tb_long_t last = (tb_long_t)filesize; + if (lua_isnumber(lua, 4)) last = (tb_long_t)lua_tonumber(lua, 4); + if (last < start - 1 || last > filesize + start - 1) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid last position(%ld)!", last); + return 2; + } + + // send file data + tb_long_t real = (tb_long_t)tb_socket_sendf(sock, rawfile, start - 1, last - start + 1); + lua_pushnumber(lua, (tb_int_t)real); + return 1; +} diff --git a/core/src/xmake/io/socket_sendto.c b/core/src/xmake/io/socket_sendto.c new file mode 100644 index 00000000000..6b1f8fa49d1 --- /dev/null +++ b/core/src/xmake/io/socket_sendto.c @@ -0,0 +1,106 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_sendto.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_sendto" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// io.socket_sendto(sock, data, addr, port) +tb_int_t xm_io_socket_sendto(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // check socket + if (!lua_isuserdata(lua, 1)) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid socket!"); + return 2; + } + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get data + tb_size_t size = 0; + tb_byte_t const* data = tb_null; + if (lua_istable(lua, 2)) + { + // get data address + lua_pushstring(lua, "data"); + lua_gettable(lua, 2); + data = (tb_byte_t const*)(tb_size_t)(tb_long_t)lua_tonumber(lua, -1); + lua_pop(lua, 1); + + // get data size + lua_pushstring(lua, "size"); + lua_gettable(lua, 2); + size = (tb_size_t)lua_tonumber(lua, -1); + lua_pop(lua, 1); + } + else + { + size_t datasize = 0; + data = (tb_byte_t const*)luaL_checklstring(lua, 2, &datasize); + size = (tb_size_t)datasize; + } + if (!data || !size) + { + lua_pushnumber(lua, -1); + lua_pushfstring(lua, "invalid data(%p) and size(%zu)!", data, size); + return 2; + } + + // get address + tb_char_t const* addr = lua_tostring(lua, 3); + tb_uint16_t port = (tb_uint16_t)luaL_checknumber(lua, 4); + if (!addr || !port) + { + lua_pushnumber(lua, -1); + lua_pushliteral(lua, "invalid address!"); + return 2; + } + + // get address family + tb_size_t family = (tb_size_t)luaL_checknumber(lua, 5); + + // init ip address + tb_ipaddr_t ipaddr; + tb_ipaddr_set(&ipaddr, addr, port, (tb_uint8_t)family); + + // send data + tb_long_t real = tb_socket_usend(sock, &ipaddr, data, size); + lua_pushnumber(lua, (tb_int_t)real); + return 1; +} diff --git a/core/src/xmake/io/socket_wait.c b/core/src/xmake/io/socket_wait.c new file mode 100644 index 00000000000..d9b845785e5 --- /dev/null +++ b/core/src/xmake/io/socket_wait.c @@ -0,0 +1,61 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015 - 2019, TBOOX Open Source Group. + * + * @author ruki + * @file socket_wait.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "socket_wait" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.socket_wait(sock, events, timeout) +tb_int_t xm_io_socket_wait(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get socket + tb_socket_ref_t sock = (tb_socket_ref_t)lua_touserdata(lua, 1); + tb_check_return_val(sock, 0); + + // get events + tb_size_t events = (tb_size_t)luaL_checknumber(lua, 2); + + // get timeout + tb_long_t timeout = (tb_long_t)luaL_checknumber(lua, 3); + + // wait socket + lua_pushnumber(lua, (tb_int_t)tb_socket_wait(sock, events, timeout)); + return 1; +} + diff --git a/core/src/xmake/io/stdfile.c b/core/src/xmake/io/stdfile.c index c5adae14354..8f466fa7a72 100644 --- a/core/src/xmake/io/stdfile.c +++ b/core/src/xmake/io/stdfile.c @@ -167,6 +167,6 @@ tb_int_t xm_io_stdfile(lua_State* lua) lua_pushlightuserdata(lua, (tb_pointer_t)file); return 1; } - else xm_io_file_return_error(lua, "invalid stdfile type!"); + else xm_io_return_error(lua, "invalid stdfile type!"); } diff --git a/core/src/xmake/machine.c b/core/src/xmake/machine.c index b98e6023693..835e10b7337 100644 --- a/core/src/xmake/machine.c +++ b/core/src/xmake/machine.c @@ -109,6 +109,21 @@ tb_int_t xm_io_filelock_unlock(lua_State* lua); tb_int_t xm_io_filelock_trylock(lua_State* lua); tb_int_t xm_io_filelock_close(lua_State* lua); +// the io/socket functions +tb_int_t xm_io_socket_open(lua_State* lua); +tb_int_t xm_io_socket_rawfd(lua_State* lua); +tb_int_t xm_io_socket_wait(lua_State* lua); +tb_int_t xm_io_socket_bind(lua_State* lua); +tb_int_t xm_io_socket_listen(lua_State* lua); +tb_int_t xm_io_socket_accept(lua_State* lua); +tb_int_t xm_io_socket_connect(lua_State* lua); +tb_int_t xm_io_socket_send(lua_State* lua); +tb_int_t xm_io_socket_sendto(lua_State* lua); +tb_int_t xm_io_socket_sendfile(lua_State* lua); +tb_int_t xm_io_socket_recv(lua_State* lua); +tb_int_t xm_io_socket_recvfrom(lua_State* lua); +tb_int_t xm_io_socket_close(lua_State* lua); + // the path functions tb_int_t xm_path_relative(lua_State* lua); tb_int_t xm_path_absolute(lua_State* lua); @@ -241,6 +256,19 @@ static luaL_Reg const g_io_functions[] = , { "filelock_trylock", xm_io_filelock_trylock } , { "filelock_unlock", xm_io_filelock_unlock } , { "filelock_close", xm_io_filelock_close } +, { "socket_open", xm_io_socket_open } +, { "socket_rawfd", xm_io_socket_rawfd } +, { "socket_wait", xm_io_socket_wait } +, { "socket_bind", xm_io_socket_bind } +, { "socket_listen", xm_io_socket_listen } +, { "socket_accept", xm_io_socket_accept } +, { "socket_connect", xm_io_socket_connect } +, { "socket_send", xm_io_socket_send } +, { "socket_sendto", xm_io_socket_sendto } +, { "socket_sendfile", xm_io_socket_sendfile } +, { "socket_recv", xm_io_socket_recv } +, { "socket_recvfrom", xm_io_socket_recvfrom } +, { "socket_close", xm_io_socket_close } , { tb_null, tb_null } }; diff --git a/core/src/xmake/makefile b/core/src/xmake/makefile index 9961231915d..e2660128ac2 100644 --- a/core/src/xmake/makefile +++ b/core/src/xmake/makefile @@ -60,6 +60,19 @@ xmake_C_FILES += \ io/filelock_unlock \ io/filelock_trylock \ io/filelock_close \ + io/socket_open \ + io/socket_rawfd \ + io/socket_wait \ + io/socket_bind \ + io/socket_listen \ + io/socket_accept \ + io/socket_connect \ + io/socket_send \ + io/socket_sendto \ + io/socket_sendfile \ + io/socket_recv \ + io/socket_recvfrom \ + io/socket_close \ path/relative \ path/absolute \ path/translate \ diff --git a/tests/modules/bytes/test.lua b/tests/modules/bytes/test.lua new file mode 100644 index 00000000000..52b754c0af0 --- /dev/null +++ b/tests/modules/bytes/test.lua @@ -0,0 +1,37 @@ + +function test_ctor(t) + t:are_equal(bytes("123456789"):str(), "123456789") + t:are_equal(bytes(bytes("123456789")):str(), "123456789") + t:are_equal(bytes(bytes("123456789"), 3, 5):str(), "345") + t:are_equal(bytes("123456789"):size(), 9) + t:are_equal(bytes(10):size(), 10) + t:are_equal(bytes(bytes("123"), bytes("456"), bytes("789")):str(), "123456789") + t:are_equal(bytes({bytes("123"), bytes("456"), bytes("789")}):str(), "123456789") +end + +function test_clone(t) + t:are_equal(bytes(10):clone():size(), 10) + t:are_equal(bytes("123456789"):clone():str(), "123456789") +end + +function test_slice(t) + t:are_equal(bytes(10):slice(1, 2):size(), 2) + t:are_equal(bytes("123456789"):slice(1, 4):str(), "1234") +end + +function test_index(t) + local b = bytes("123456789") + t:are_equal(b[{1, 4}]:str(), "1234") + t:will_raise(function() b[1] = string.byte('2') end) + b = bytes(9) + b[{1, 9}] = bytes("123456789") + t:are_equal(b:str(), "123456789") + b[1] = string.byte('2') + t:are_equal(b:str(), "223456789") + t:will_raise(function() b[100] = string.byte('2') end) +end + +function test_concat(t) + t:are_equal((bytes("123") .. bytes("456")):str(), "123456") + t:are_equal(bytes(bytes("123"), bytes("456")):str(), "123456") +end diff --git a/tests/modules/socket/tcp/echo_client.lua b/tests/modules/socket/tcp/echo_client.lua new file mode 100644 index 00000000000..f3d6d9baeaf --- /dev/null +++ b/tests/modules/socket/tcp/echo_client.lua @@ -0,0 +1,21 @@ +import("core.base.socket") + +function main() + local addr = "127.0.0.1" + local port = 9001 + print("connect %s:%d ..", addr, port) + local sock = socket.connect(addr, port) + print("%s: connected!", sock) + local count = 0 + while count < 10000 do + local send = sock:send("hello world..", {block = true}) + if send > 0 then + sock:recv(13, {block = true}) + else + break + end + count = count + 1 + end + print("%s: send ok, count: %d!", sock, count) + sock:close() +end diff --git a/tests/modules/socket/tcp/echo_server.lua b/tests/modules/socket/tcp/echo_server.lua new file mode 100644 index 00000000000..9ff19dbed8b --- /dev/null +++ b/tests/modules/socket/tcp/echo_server.lua @@ -0,0 +1,32 @@ +import("core.base.socket") + +function main() + + local addr = "127.0.0.1" + local port = 9001 + local sock = socket.bind(addr, port) + sock:listen(20) + print("%s: listening %s:%d ..", sock, addr, port) + while true do + local sock_client = sock:accept() + if sock_client then + print("%s: accepted", sock_client) + local count = 0 + local result = nil + while true do + local recv, data = sock_client:recv(13, {block = true}) + if recv > 0 then + result = data + sock_client:send(data, {block = true}) + count = count + 1 + else + break + end + end + print("%s: recv: %d, count: %d", sock_client, result and result:size() or 0, count) + result:dump() + sock_client:close() + end + end + sock:close() +end diff --git a/tests/modules/socket/tcp/file_client.lua b/tests/modules/socket/tcp/file_client.lua new file mode 100644 index 00000000000..42532f5fe74 --- /dev/null +++ b/tests/modules/socket/tcp/file_client.lua @@ -0,0 +1,35 @@ +import("core.base.socket") + +function main() + local addr = "127.0.0.1" + local port = 9090 + print("connect %s:%d ..", addr, port) + local sock = socket.connect(addr, port) + print("%s: connected!", sock) + local real = 0 + local recv = 0 + local data = nil + local wait = false + local results = {} + while true do + real, data = sock:recv(8192) + if real > 0 then + recv = recv + real + wait = false + table.insert(results, data) + elseif real == 0 and not wait then + if sock:wait(socket.EV_RECV, -1) == socket.EV_RECV then + wait = true + else + break + end + else + break + end + end + if #results > 0 then + data = bytes(results) + end + print("%s: recv ok, size: %d, #data: %d!", sock, recv, data and data:size() or 0) + sock:close() +end diff --git a/tests/modules/socket/tcp/file_server.lua b/tests/modules/socket/tcp/file_server.lua new file mode 100644 index 00000000000..d619a496f7e --- /dev/null +++ b/tests/modules/socket/tcp/file_server.lua @@ -0,0 +1,24 @@ +import("core.base.socket") + +function main(filepath) + + local addr = "127.0.0.1" + local port = 9090 + local sock = socket.bind(addr, port) + sock:listen(20) + print("%s: listening %s:%d ..", sock, addr, port) + while true do + local sock_client = sock:accept() + if sock_client then + print("%s: accepted", sock_client) + local file = io.open(filepath, 'rb') + if file then + local send = sock_client:sendfile(file, {block = true}) + print("%s: send %s %d bytes!", sock_client, filepath, send) + file:close() + end + sock_client:close() + end + end + sock:close() +end diff --git a/tests/modules/socket/udp/echo_client.lua b/tests/modules/socket/udp/echo_client.lua new file mode 100644 index 00000000000..f46ae471620 --- /dev/null +++ b/tests/modules/socket/udp/echo_client.lua @@ -0,0 +1,15 @@ +import("core.base.socket") + +function main(data) + local addr = "127.0.0.1" + local port = 9001 + local sock = socket.udp() + local send = sock:sendto(data or "hello xmake!", addr, port, {block = true}) + print("%s: send to %s:%d %d bytes!", sock, addr, port, send) + local recv, data, peer_addr, peer_port = sock:recvfrom(8112, {block = true}) + if recv > 0 then + print("%s: recv %d bytes from %s:%d", sock, recv, peer_addr, peer_port) + data:dump() + end + sock:close() +end diff --git a/tests/modules/socket/udp/echo_server.lua b/tests/modules/socket/udp/echo_server.lua new file mode 100644 index 00000000000..84f55eae475 --- /dev/null +++ b/tests/modules/socket/udp/echo_server.lua @@ -0,0 +1,18 @@ +import("core.base.socket") + +function main() + local addr = "127.0.0.1" + local port = 9001 + local sock = socket.udp() + sock:bind(addr, port) + while true do + print("%s: recv in %s:%d ..", sock, addr, port) + local recv, data, peer_addr, peer_port = sock:recvfrom(8192, {block = true}) + print("%s: recv %d bytes from: %s:%d", sock, recv, peer_addr, peer_port) + if data then + data:dump() + sock:sendto(data, peer_addr, peer_port) + end + end + sock:close() +end diff --git a/xmake/core/base/bytes.lua b/xmake/core/base/bytes.lua new file mode 100644 index 00000000000..6c62697aa22 --- /dev/null +++ b/xmake/core/base/bytes.lua @@ -0,0 +1,441 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015 - 2019, TBOOX Open Source Group. +-- +-- @author ruki +-- @file bytes.lua +-- + +-- define module: bytes +local bytes = bytes or {} +local _instance = _instance or {} + +-- load modules +local bit = require('bit') +local ffi = require('ffi') +local os = require("base/os") +local utils = require("base/utils") + +-- define ffi interfaces +ffi.cdef[[ + void* malloc(size_t size); + void free(void* data); +]] + +-- new a bytes instance +-- +-- bytes(size): allocates a buffer of given size +-- bytes(size, ptr [, manage]): mounts buffer on existing storage (manage memory or not) +-- bytes(str): mounts a buffer from the given string +-- bytes(bytes, start, last): mounts a buffer from another one, with start/last limits +-- bytes(bytes1, bytes2, bytes3, ...): allocates and concat buffer from list of byte buffers +-- bytes(bytes): allocates a buffer from another one (strict replica, sharing memory) +-- bytes({bytes1, bytes2, ...}): allocates and concat buffer from a list of byte buffers (table) +-- +function _instance.new(...) + local args = {...} + local arg1, arg2, arg3 = unpack(args) + local instance = table.inherit(_instance) + if type(arg1) == "number" then + local size = arg1 + local ptr = arg2 + if ptr then + -- bytes(size, ptr [, manage]): mounts buffer on existing storage (manage memory or not) + local manage = arg3 + if manage then + instance._CDATA = ffi.gc(ffi.cast("unsigned char*", ptr), ffi.C.free) + instance._MANAGED = true + else + instance._CDATA = ffi.cast("unsigned char*", ptr) + instance._MANAGED = false + end + else + -- bytes(size): allocates a buffer of given size + ptr = ffi.C.malloc(size) + instance._CDATA = ffi.gc(ffi.cast("unsigned char*", ptr), ffi.C.free) + instance._MANAGED = true + end + instance._SIZE = size + instance._READONLY = false + elseif type(arg1) == "string" then + -- bytes(str): mounts a buffer from the given string + local str = arg1 + instance._SIZE = #str + instance._CDATA = ffi.cast("unsigned char*", str) + instance._REF = str -- keep ref for GC + instance._MANAGED = false + instance._READONLY = true + elseif type(arg1) == "table" then + if type(arg2) == 'number' then + -- bytes(bytes, start, last): mounts a buffer from another one, with start/last limits: + local b = arg1 + local start = arg2 or 1 + local last = arg3 or b:size() + if start < 1 or last > b:size() then + os.raise("incorrect bounds(%d-%d) for bytes(...)!", start, last) + end + instance._SIZE = last - start + 1 + instance._CDATA = b:cdata() - 1 + start + instance._REF = b -- keep lua ref for GC + instance._MANAGED = false + instance._READONLY = b:readonly() + elseif type(arg2) == "table" then + -- bytes(bytes1, bytes2, bytes3, ...): allocates and concat buffer from list of byte buffers + instance._SIZE = 0 + for _, b in ipairs(args) do + instance._SIZE = instance._SIZE + b:size() + end + instance._CDATA = ffi.gc(ffi.cast("unsigned char*", ffi.C.malloc(instance._SIZE)), ffi.C.free) + local offset = 0 + for _, b in ipairs(args) do + ffi.copy(instance._CDATA + offset, b:cdata(), b:size()) + offset = offset + b:size() + end + instance._MANAGED = true + instance._READONLY = false + elseif not arg2 and arg1[1] and type(arg1[1]) == 'table' then + -- bytes({bytes1, bytes2, ...}): allocates and concat buffer from a list of byte buffers (table) + args = arg1 + instance._SIZE = 0 + for _, b in ipairs(args) do + instance._SIZE = instance._SIZE + b:size() + end + instance._CDATA = ffi.gc(ffi.cast("unsigned char*", ffi.C.malloc(instance._SIZE)), ffi.C.free) + local offset = 0 + for _, b in ipairs(args) do + ffi.copy(instance._CDATA + offset, b._CDATA, b:size()) + offset = offset + b:size() + end + instance._MANAGED = true + instance._READONLY = false + elseif not arg2 and arg1:size() then + -- bytes(bytes): allocates a buffer from another one (strict replica, sharing memory) + local b = arg1 + local start = 1 + local last = arg3 or b:size() + if start < 1 or last > b:size() then + os.raise("incorrect bounds(%d-%d)!", start, last) + end + instance._SIZE = last - start + 1 + instance._CDATA = b:cdata() - 1 + start + instance._REF = b -- keep lua ref for GC + instance._MANAGED = false + instance._READONLY = b:readonly() + end + end + if instance:cdata() == nil then + os.raise("invalid arguments for bytes(...)!") + end + setmetatable(instance, _instance) + return instance +end + +-- get bytes size +function _instance:size() + return self._SIZE +end + +-- get bytes data +function _instance:cdata() + return self._CDATA +end + +-- get data address +function _instance:caddr() + return tonumber(ffi.cast('long', self:cdata())) +end + +-- readonly? +function _instance:readonly() + return self._READONLY +end + +-- bytes:ipairs() +function _instance:ipairs() + local index = 0 + return function (...) + if index < self:size() then + index = index + 1 + return index, self[index] + end + end +end + +-- get a slice of bytes +function _instance:slice(start, last) + return bytes(self, start, last) +end + +-- copy bytes +function _instance:copy(src) + if self:readonly() then + os.raise("%s: cannot be modified!", self) + end + if type(src) == 'string' then + src = bytes(src) + end + if src:size() ~= self:size() then + os.raise("%s: cannot copy bytes, src and dst must have same size(%d->%d)!", self, src:size(), self:size()) + end + ffi.copy(self:cdata(), src:cdata(), self:size()) + return self +end + +-- clone a new bytes buffer +function _instance:clone() + local new = bytes(self:size()) + new:copy(self) + return new +end + +-- dump whole bytes data +function _instance:dump() + + local i = 0 + local n = 147 + local p = 0 + local e = self:size() + local line = nil + while p < e do + line = "" + if p + 0x20 <= e then + + -- dump offset + line = line .. string.format("${yellow}%08X ${green}", p) + + -- dump data + for i = 0, 0x20 - 1 do + if (i % 4) == 0 then + line = line .. " " + end + line = line .. string.format(" %02X", self[p + i + 1]) + end + + -- dump spaces + line = line .. " " + + -- dump characters + line = line .. "${magenta}" + for i = 0, 0x20 - 1 do + local v = self[p + i + 1] + if v > 0x1f and v < 0x7f then + line = line .. string.format("%c", v) + else + line = line .. '.' + end + end + line = line .. "${clear}" + + -- dump line + utils.cprint(line) + + -- next line + p = p + 0x20 + + elseif p < e then + + -- init padding + local padding = n - 0x20 + + -- dump offset + line = line .. string.format("${yellow}%08X ${green}", p) + if padding >= 9 then + padding = padding - 9 + end + + -- dump data + local left = e - p + for i = 0, left - 1 do + if (i % 4) == 0 then + line = line .. " " + if padding then + padding = padding - 1 + end + end + line = line .. string.format(" %02X", self[p + i + 1]) + if padding >= 3 then + padding = padding - 3 + end + end + + -- dump spaces + while padding > 0 do + line = line .. " " + padding = padding - 1 + end + + -- dump characters + line = line .. "${magenta}" + for i = 0, left - 1 do + local v = self[p + i + 1] + if v > 0x1f and v < 0x7f then + line = line .. string.format("%c", v) + else + line = line .. '.' + end + end + line = line .. "${clear}" + + + -- dump line + utils.cprint(line) + + -- next line + p = p + left + + else + break + end + end +end + +-- convert bytes to string +function _instance:str(i, j) + local offset = i and i - 1 or 0 + return ffi.string(self:cdata() + offset, (j or self:size()) - offset) +end + +-- get uint8 value +function _instance:u8(offset) + return self[offset] +end + +-- get sint8 value +function _instance:s8(offset) + local value = self[offset] + return value < 0x80 and value or -0x100 + value +end + +-- get uint16 little-endian value +function _instance:u16le(offset) + return bit.lshift(self[offset + 1], 8) + self[offset] +end + +-- get uint16 big-endian value +function _instance:u16be(offset) + return bit.lshift(self[offset], 8) + self[offset + 1] +end + +-- get sint16 little-endian value +function _instance:s16le(offset) + local value = self:u16le(offset) + return value < 0x8000 and value or -0x10000 + value +end + +-- get sint16 big-endian value +function _instance:s16be(offset) + local value = self:u16be(offset) + return value < 0x8000 and value or -0x10000 + value +end + +-- get uint32 little-endian value +function _instance:u32le(offset) + return self[offset + 3] * 0x1000000 + bit.lshift(self[offset + 2], 16) + bit.lshift(self[offset + 1], 8) + self[offset] +end + +-- get uint32 big-endian value +function _instance:u32be(offset) + return self[offset] * 0x1000000 + bit.lshift(self[offset + 1], 16) + bit.lshift(self[offset + 2], 8) + self[offset + 3] +end + +-- get sint32 little-endian value +function _instance:s32le(offset) + return bit.tobit(self:u32le(offset)) +end + +-- get sint32 big-endian value +function _instance:s32be(offset) + return bit.tobit(self:u32be(offset)) +end + +-- get byte or bytes slice at the given index position +-- +-- bytes[1] +-- bytes[{1, 2}] +-- +function _instance:__index(key) + if type(key) == "number" then + if key < 1 or key > self:size() then + os.raise("%s: index(%d/%d) out of bounds!", self, key, self:size()) + end + return self._CDATA[key - 1] + elseif type(key) == "table" then + local start, last = key[1], key[2] + return self:slice(start, last) + end + return rawget(self, key) +end + +-- set byte or bytes slice at the given index position +-- +-- bytes[1] = 0x1 +-- bytes[{1, 2}] = bytes(2) +-- +function _instance:__newindex(key, value) + if self:readonly() then + os.raise("%s: cannot modify value at index[%s]!", self, key) + end + if type(key) == "number" then + if key < 1 or key > self:size() then + os.raise("%s: index(%d/%d) out of bounds!", self, key, self:size()) + end + self._CDATA[key - 1] = value + return + elseif type(key) == "table" then + local start, last = key[1], key[2] + self:slice(start,last):copy(value) + return + end + rawset(self, key, value) +end + +-- concat two bytes buffer +function _instance:__concat(other) + local new = bytes(self:size() + other:size()) + new:slice(1, self:size()):copy(self) + new:slice(self:size() + 1, new:size()):copy(other) + return new +end + +-- tostring(bytes) +function _instance:__tostring() + local parts = {} + local size = self:size() + if size > 8 then + size = 8 + end + for i = 1, size do + parts[i] = "0x" .. bit.tohex(self[i], 2) + end + return " 8 and "..>" or ">") +end + +-- new an bytes instance +function bytes.new(...) + return _instance.new(...) +end + +-- register call function +setmetatable(bytes, { + __call = function (_, ...) + return bytes.new(...) + end, + __tostring = function() + return "" + end +}) + +-- return module: bytes +return bytes diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua index f6c93197034..6dc13ca3041 100644 --- a/xmake/core/base/io.lua +++ b/xmake/core/base/io.lua @@ -55,10 +55,15 @@ end -- close file function _file:close() - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end - local ok, errors = io.file_close(self._FILE) + + -- close file + ok, errors = io.file_close(self._FILE) if ok then self._FILE = nil end @@ -67,7 +72,11 @@ end -- tostring(file) function _file:__tostring() - return "file: " .. self:name() + local str = self:path() + if #str > 16 then + str = ".." .. str:sub(#str - 16, #str) + end + return "" end -- gc(file) @@ -84,101 +93,142 @@ end -- get file rawfd function _file:rawfd() - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return nil, errors end + + -- get file rawfd local result, errors = io.file_rawfd(self._FILE) if not result and errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return result, errors end -- get file size function _file:size() - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return nil, errors end + + -- get file size local result, errors = io.file_size(self._FILE) if not result and errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return result, errors end -- read data from file function _file:read(fmt, opt) - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return nil, errors end + + -- read file opt = opt or {} local result, errors = io.file_read(self._FILE, fmt, opt.continuation) if errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return result, errors end -- write data to file function _file:write(...) - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end - local ok, errors = io.file_write(self._FILE, ...) + + -- write file + ok, errors = io.file_write(self._FILE, ...) if not ok and errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return ok, errors end -- seek offset at file function _file:seek(whence, offset) - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end + + -- seek file local result, errors = io.file_seek(self._FILE, whence, offset) if not result and errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return result, errors end -- flush data to file function _file:flush() - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end - local ok, errors = io.file_flush(self._FILE) + + -- flush file + ok, errors = io.file_flush(self._FILE) if not ok and errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return ok, errors end -- this file is a tty? function _file:isatty() - if not self._FILE then - return false, string.format("file(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return nil, errors end - local ok, errors = io.file_isatty(self._FILE) + + -- is a tty? + ok, errors = io.file_isatty(self._FILE) if ok == nil and errors then - errors = string.format("file(%s): %s", self:name(), errors) + errors = string.format("%s: %s", self, errors) end return ok, errors end --- iterator of lines -function _file._lines_iter(data) - local l = data.file:read("l", data.opt) - if not l and data.opt.close_on_finished then - data.file:close() +-- ensure the file is opened +function _file:_ensure_opened() + if not self._FILE then + return false, string.format("%s: has been closed!", self) end - return l + return true end --- read all lines from a file +-- read all lines from file function _file:lines(opt) - return _file._lines_iter, { file = assert(self), opt = opt or {} } + opt = opt or {} + return function() + local l = self:read("l", opt) + if not l and opt.close_on_finished then + self:close() + end + return l + end end -- print file @@ -245,14 +295,19 @@ end -- @return ok, errors -- function _filelock:lock(opt) - if not self._LOCK then - return false, string.format("filelock(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end + + -- lock it if self._LOCKED_NUM > 0 or io.filelock_lock(self._LOCK, opt) then self._LOCKED_NUM = self._LOCKED_NUM + 1 return true else - return false, string.format("filelock(%s): lock %s failed!", self:name(), self:path()) + return false, string.format("%s: lock failed!", self) end end @@ -263,22 +318,32 @@ end -- @return ok, errors -- function _filelock:trylock(opt) - if not self._LOCK then - return false, string.format("filelock(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end + + -- try lock it if self._LOCKED_NUM > 0 or io.filelock_trylock(self._LOCK, opt) then self._LOCKED_NUM = self._LOCKED_NUM + 1 return true else - return false, string.format("filelock(%s): trylock %s failed!", self:name(), self:path()) + return false, string.format("%s: trylock failed!", self) end end -- unlock file function _filelock:unlock(opt) - if not self._LOCK then - return false, string.format("filelock(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end + + -- unlock it if self._LOCKED_NUM > 1 or (self._LOCKED_NUM > 0 and io.filelock_unlock(self._LOCK)) then if self._LOCKED_NUM > 0 then self._LOCKED_NUM = self._LOCKED_NUM - 1 @@ -287,16 +352,21 @@ function _filelock:unlock(opt) end return true else - return false, string.format("filelock(%s): unlock %s failed!", self:name(), self:path()) + return false, string.format("%s: unlock failed!", self) end end -- close filelock function _filelock:close() - if not self._LOCK then - return false, string.format("filelock(%s) has been closed!", self:name()) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors end - local ok = io.filelock_close(self._LOCK) + + -- close it + ok = io.filelock_close(self._LOCK) if ok then self._LOCK = nil self._LOCKED_NUM = 0 @@ -304,9 +374,21 @@ function _filelock:close() return ok end +-- ensure the file is opened +function _filelock:_ensure_opened() + if not self._LOCK then + return false, string.format("%s: has been closed!", self) + end + return true +end + -- tostring(filelock) function _filelock:__tostring() - return "filelock: " .. self:name() + local str = self:path() + if #str > 16 then + str = ".." .. str:sub(#str - 16, #str) + end + return "" end -- gc(filelock) diff --git a/xmake/core/base/socket.lua b/xmake/core/base/socket.lua new file mode 100644 index 00000000000..4a2dfdcdc27 --- /dev/null +++ b/xmake/core/base/socket.lua @@ -0,0 +1,581 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015 - 2019, TBOOX Open Source Group. +-- +-- @author ruki +-- @file socket.lua +-- + +-- define module +local socket = socket or {} +local _instance = _instance or {} + +-- load modules +local io = require("base/io") +local bytes = require("base/bytes") +local table = require("base/table") +local string = require("base/string") + +-- the socket types +socket.TCP = 1 +socket.UDP = 2 +socket.ICMP = 3 + +-- the socket families +socket.IPV4 = 1 +socket.IPV6 = 2 + +-- the socket events +socket.EV_RECV = 1 +socket.EV_SEND = 2 +socket.EV_CONN = socket.EV_SEND +socket.EV_ACPT = socket.EV_RECV + +-- new a socket +function _instance.new(socktype, family, sock) + local instance = table.inherit(_instance) + instance._SOCK = sock + instance._TYPE = socktype + instance._FAMILY = family + setmetatable(instance, _instance) + return instance +end + +-- get socket type +function _instance:type() + return self._TYPE +end + +-- get socket family +function _instance:family() + return self._FAMILY +end + +-- get socket rawfd +function _instance:rawfd() + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return nil, errors + end + + -- get rawfd + local result, errors = io.socket_rawfd(self._SOCK) + if not result and errors then + errors = string.format("%s: %s", self, errors) + end + return result, errors +end + +-- bind socket +function _instance:bind(addr, port) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- bind it + local ok, errors = io.socket_bind(self._SOCK, addr, port, self:family()) + if not ok and errors then + errors = string.format("%s: %s", self, errors) + end + return ok, errors +end + +-- listen socket +function _instance:listen(backlog) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- listen it + local ok, errors = io.socket_listen(self._SOCK, backlog or 10) + if not ok and errors then + errors = string.format("%s: %s", self, errors) + end + return ok, errors +end + +-- accept socket +function _instance:accept(opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- accept it + local sock, errors = io.socket_accept(self._SOCK) + if not sock and not errors then + opt = opt or {} + local events, waiterrs = self:wait(socket.EV_ACPT, opt.timeout or -1) + if events == socket.EV_ACPT then + sock, errors = io.socket_accept(self._SOCK) + else + errors = waiterrs + end + end + if not sock and errors then + errors = string.format("%s: %s", self, errors) + end + if sock then + sock = _instance.new(self:type(), self:family(), sock) + end + return sock, errors +end + +-- connect socket +function _instance:connect(addr, port, opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- connect it + local ok, errors = io.socket_connect(self._SOCK, addr, port, self:family()) + if ok == 0 then + opt = opt or {} + local events, waiterrs = self:wait(socket.EV_CONN, opt.timeout or -1) + if events == socket.EV_CONN then + ok, errors = io.socket_connect(self._SOCK, addr, port, self:family()) + else + errors = waiterrs + end + end + if ok < 0 and errors then + errors = string.format("%s: %s", self, errors) + end + return ok, errors +end + +-- send data to socket +function _instance:send(data, opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- data is bytes? unpack the raw address + local datasize = #data + if type(data) == "table" and data.caddr then + datasize = data:size() + data = {data = data:caddr(), size = data:size()} + end + + -- init start and last + opt = opt or {} + local start = opt.start or 1 + local last = opt.last or datasize + + -- check start and last + if start > last or start < 1 then + return -1, string.format("%s: invalid start(%d) and last(%d)!", self, start, last) + end + + -- send it + local send = 0 + local real = 0 + local wait = false + local errors = nil + if opt.block then + local size = last + 1 - start + while start <= last do + real, errors = io.socket_send(self._SOCK, data, start, last) + if real > 0 then + send = send + real + start = start + real + wait = false + elseif real == 0 and not wait then + local events, waiterrs = self:wait(socket.EV_SEND, opt.timeout or -1) + if events == socket.EV_SEND then + wait = true + else + errors = waiterrs + break + end + else + break + end + end + if send ~= size then + send = -1 + end + else + send, errors = io.socket_send(self._SOCK, data, start, last) + if send < 0 and errors then + errors = string.format("%s: %s", self, errors) + end + end + return send, errors +end + +-- send file to socket +function _instance:sendfile(file, opt) + + -- ensure the socket opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- ensure the file opened + local ok, errors = file:_ensure_opened() + if not ok then + return -1, errors + end + + -- init start and last + opt = opt or {} + local start = opt.start or 1 + local last = opt.last or file:size() + + -- check start and last + if start > last or start < 1 then + return -1, string.format("%s: invalid start(%d) and last(%d)!", self, start, last) + end + + -- send it + local send = 0 + local real = 0 + local wait = false + local errors = nil + if opt.block then + local size = last + 1 - start + while start <= last do + real, errors = io.socket_sendfile(self._SOCK, file._FILE, start, last) + if real > 0 then + send = send + real + start = start + real + wait = false + elseif real == 0 and not wait then + local events, waiterrs = self:wait(socket.EV_SEND, opt.timeout or -1) + if events == socket.EV_SEND then + wait = true + else + errors = waiterrs + break + end + else + break + end + end + if send ~= size then + send = -1 + end + else + send, errors = io.socket_sendfile(self._SOCK, file._FILE, start, last) + if send < 0 and errors then + errors = string.format("%s: %s", self, errors) + end + end + return send, errors +end + +-- recv data from socket +function _instance:recv(size, opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- check size + if size == 0 then + return 0 + elseif size == nil or size < 0 then + return -1, string.format("%s: invalid size(%d)!", self, size) + end + + -- recv it + opt = opt or {} + local recv = 0 + local real = 0 + local wait = false + local data_or_errors = nil + if opt.block then + local results = {} + while recv < size do + real, data_or_errors = io.socket_recv(self._SOCK, size - recv) + if real > 0 then + recv = recv + real + wait = false + table.insert(results, bytes(data_or_errors)) + elseif real == 0 and not wait then + local events, waiterrs = self:wait(socket.EV_RECV, opt.timeout or -1) + if events == socket.EV_RECV then + wait = true + else + data_or_errors = waiterrs + break + end + else + break + end + end + if recv == size then + data_or_errors = bytes(results) + else + recv = -1 + end + else + recv, data_or_errors = io.socket_recv(self._SOCK, size) + if recv > 0 then + data_or_errors = bytes(data_or_errors) + end + end + if recv < 0 and data_or_errors then + data_or_errors = string.format("%s: %s", self, data_or_errors) + end + return recv, data_or_errors +end + +-- send udp data to peer +function _instance:sendto(data, addr, port, opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- only for udp + if self:type() ~= socket.UDP then + return -1, string.format("%s: sendto() only for udp socket!", self) + end + + -- check address + if not addr or not port then + return -1, string.format("%s: sendto empty address!", self) + end + + -- data is bytes? unpack the raw address + if type(data) == "table" and data.caddr then + data = {data = data:caddr(), size = data:size()} + end + + -- send it + opt = opt or {} + local send = 0 + local wait = false + local errors = nil + if opt.block then + while true do + send, errors = io.socket_sendto(self._SOCK, data, addr, port, self:family()) + if send == 0 and not wait then + local events, waiterrs = self:wait(socket.EV_SEND, opt.timeout or -1) + if events == socket.EV_SEND then + wait = true + else + errors = waiterrs + break + end + else + break + end + end + else + send, errors = io.socket_sendto(self._SOCK, data, addr, port, self:family()) + if send < 0 and errors then + errors = string.format("%s: %s", self, errors) + end + end + return send, errors +end + +-- recv udp data from peer +function _instance:recvfrom(size, opt) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- only for udp + if self:type() ~= socket.UDP then + return -1, string.format("%s: sendto() only for udp socket!", self) + end + + -- check size + if size == 0 then + return 0 + elseif size == nil or size < 0 then + return -1, string.format("%s: invalid size(%d)!", self, size) + end + + -- recv it + opt = opt or {} + local recv = 0 + local wait = false + local data_or_errors = nil + if opt.block then + while true do + recv, data_or_errors, addr, port = io.socket_recvfrom(self._SOCK, size) + if recv > 0 then + data_or_errors = bytes(data_or_errors) + break + elseif recv == 0 and not wait then + local events, waiterrs = self:wait(socket.EV_RECV, opt.timeout or -1) + if events == socket.EV_RECV then + wait = true + else + recv = -1 + data_or_errors = waiterrs + break + end + else + break + end + end + else + recv, data_or_errors, addr, port = io.socket_recvfrom(self._SOCK, size) + if recv > 0 then + data_or_errors = bytes(data_or_errors) + end + end + if recv < 0 and data_or_errors then + data_or_errors = string.format("%s: %s", self, data_or_errors) + end + return recv, data_or_errors, addr, port +end + +-- wait socket events +function _instance:wait(events, timeout) + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return -1, errors + end + + -- wait it + local events, errors = io.socket_wait(self._SOCK, events, timeout or -1) + if events < 0 and errors then + errors = string.format("%s: %s", self, errors) + end + return events, errors +end + +-- close socket +function _instance:close() + + -- ensure opened + local ok, errors = self:_ensure_opened() + if not ok then + return false, errors + end + + -- close it + ok = io.socket_close(self._SOCK) + if ok then + self._SOCK = nil + end + return ok +end + +-- ensure the socket is opened +function _instance:_ensure_opened() + if not self._SOCK then + return false, string.format("%s: has been closed!", self) + end + return true +end + +-- tostring(socket) +function _instance:__tostring() + local rawfd = self:rawfd() or "closed" + local types = {"tcp", "udp", "icmp"} + return string.format("", types[self:type()], self:family() == socket.IPV6 and "6" or "4", rawfd) +end + +-- gc(socket) +function _instance:__gc() + if self._SOCK and io.socket_close(self._SOCK) then + self._SOCK = nil + end +end + +-- open a socket +-- +-- @param socktype the socket type, e.g. tcp, udp, icmp +-- @param family the address family, e.g. ipv4, ipv6 +-- +-- @return the socket instance +-- +function socket.open(socktype, family) + socktype = socktype or socket.TCP + family = family or socket.IPV4 + local sock, errors = io.socket_open(socktype, family) + if sock then + return _instance.new(socktype, family, sock) + else + return nil, errors or string.format("failed to open socket(%s/%s)!", socktype, family) + end +end + +-- open tcp socket +function socket.tcp(opt) + opt = opt or {} + return socket.open(socket.TCP, opt.family or socket.IPV4) +end + +-- open udp socket +function socket.udp(opt) + opt = opt or {} + return socket.open(socket.UDP, opt.family or socket.IPV4) +end + +-- open and bind tcp socket +function socket.bind(addr, port, opt) + local sock, errors = socket.tcp(opt) + if not sock then + return nil, errors + end + local ok, errors = sock:bind(addr, port) + if not ok then + sock:close() + return nil, string.format("bind %s:%s failed, errors: %s!", addr, port, errors or "") + end + return sock +end + +-- open and connect tcp socket +function socket.connect(addr, port, opt) + local sock, errors = socket.tcp(opt) + if not sock then + return nil, errors + end + local ok, errors = sock:connect(addr, port, opt) + if ok <= 0 then + sock:close() + return nil, string.format("connect %s:%s failed, errors: %s!", addr, port, errors or "") + end + return sock +end + +-- return module +return socket diff --git a/xmake/core/sandbox/modules/bytes.lua b/xmake/core/sandbox/modules/bytes.lua new file mode 100644 index 00000000000..c1cbbcb2a4a --- /dev/null +++ b/xmake/core/sandbox/modules/bytes.lua @@ -0,0 +1,23 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015 - 2019, TBOOX Open Source Group. +-- +-- @author ruki +-- @file bytes.lua +-- + +-- load modules +return require("base/bytes") + diff --git a/xmake/core/sandbox/modules/import/core/base/socket.lua b/xmake/core/sandbox/modules/import/core/base/socket.lua new file mode 100644 index 00000000000..73aa0994b63 --- /dev/null +++ b/xmake/core/sandbox/modules/import/core/base/socket.lua @@ -0,0 +1,217 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015 - 2019, TBOOX Open Source Group. +-- +-- @author ruki +-- @file socket.lua +-- + +-- load modules +local utils = require("base/utils") +local socket = require("base/socket") +local string = require("base/string") +local raise = require("sandbox/modules/raise") + +-- define module +local sandbox_core_base_socket = sandbox_core_base_socket or {} +local sandbox_core_base_socket_instance = sandbox_core_base_socket_instance or {} + +-- export the socket types +sandbox_core_base_socket.TCP = socket.TCP +sandbox_core_base_socket.UDP = socket.UDP +sandbox_core_base_socket.ICMP = socket.ICMP + +-- export the socket families +sandbox_core_base_socket.IPV4 = socket.IPV4 +sandbox_core_base_socket.IPV6 = socket.IPV6 + +-- export the socket events +sandbox_core_base_socket.EV_RECV = socket.EV_RECV +sandbox_core_base_socket.EV_SEND = socket.EV_SEND +sandbox_core_base_socket.EV_CONN = socket.EV_SEND +sandbox_core_base_socket.EV_ACPT = socket.EV_RECV + +-- wrap socket +function _socket_wrap(sock) + + -- hook socket interfaces + local hooked = {} + for name, func in pairs(sandbox_core_base_socket_instance) do + if not name:startswith("_") and type(func) == "function" then + hooked["_" .. name] = sock["_" .. name] or sock[name] + hooked[name] = func + end + end + for name, func in pairs(hooked) do + sock[name] = func + end + return sock +end + +-- wait socket events +function sandbox_core_base_socket_instance.wait(sock, events, timeout) + local events, errors = sock:_wait(events, timeout) + if events < 0 and errors then + raise(errors) + end + return events +end + +-- bind socket +function sandbox_core_base_socket_instance.bind(sock, addr, port) + local ok, errors = sock:_bind(addr, port) + if not ok and errors then + raise(errors) + end + return ok +end + +-- listen socket +function sandbox_core_base_socket_instance.listen(sock, backlog) + local ok, errors = sock:_listen(backlog) + if not ok and errors then + raise(errors) + end + return ok +end + +-- accept socket +function sandbox_core_base_socket_instance.accept(sock, opt) + local client_sock, errors = sock:_accept(opt) + if not client_sock and errors then + raise(errors) + end + return client_sock and _socket_wrap(client_sock) or nil +end + +-- connect socket +function sandbox_core_base_socket_instance.connect(sock, addr, port, opt) + local ok, errors = sock:_connect(addr, port, opt) + if ok < 0 and errors then + raise(errors) + end + return ok +end + +-- send data to socket +function sandbox_core_base_socket_instance.send(sock, data, opt) + local real, errors = sock:_send(data, opt) + if real < 0 and errors then + raise(errors) + end + return real +end + +-- send file to socket +function sandbox_core_base_socket_instance.sendfile(sock, file, opt) + local real, errors = sock:_sendfile(file, opt) + if real < 0 and errors then + raise(errors) + end + return real +end + +-- recv data from socket +function sandbox_core_base_socket_instance.recv(sock, size, opt) + local real, data_or_errors = sock:_recv(size, opt) + if real < 0 and data_or_errors then + raise(data_or_errors) + end + return real, data_or_errors +end + +-- send udp data to peer +function sandbox_core_base_socket_instance.sendto(sock, data, addr, port, opt) + local real, errors = sock:_sendto(data, addr, port, opt) + if real < 0 and errors then + raise(errors) + end + return real +end + +-- recv udp data from peer +function sandbox_core_base_socket_instance.recvfrom(sock, size, opt) + local real, data_or_errors, addr, port = sock:_recvfrom(size, opt) + if real < 0 and data_or_errors then + raise(data_or_errors) + end + return real, data_or_errors, addr, port +end + +-- get socket rawfd +function sandbox_core_base_socket_instance.rawfd(sock) + local result, errors = sock:_rawfd() + if not result then + raise(errors) + end + return result +end + +-- close socket +function sandbox_core_base_socket_instance.close(sock) + local ok, errors = sock:_close() + if not ok then + raise(errors) + end +end + +-- open socket +function sandbox_core_base_socket.open(socktype, family) + local sock, errors = socket.open(socktype, family) + if not sock then + raise(errors) + end + return _socket_wrap(sock) +end + +-- open tcp socket +function sandbox_core_base_socket.tcp(opt) + local sock, errors = socket.tcp(opt) + if not sock then + raise(errors) + end + return _socket_wrap(sock) +end + +-- open udp socket +function sandbox_core_base_socket.udp(opt) + local sock, errors = socket.udp(opt) + if not sock then + raise(errors) + end + return _socket_wrap(sock) +end + +-- open and bind tcp socket +function sandbox_core_base_socket.bind(addr, port, opt) + local sock, errors = socket.bind(addr, port, opt) + if not sock then + raise(errors) + end + return _socket_wrap(sock) +end + +-- open and connect tcp socket +function sandbox_core_base_socket.connect(addr, port, opt) + local sock, errors = socket.connect(addr, port, opt) + if not sock then + raise(errors) + end + return _socket_wrap(sock) +end + +-- return module +return sandbox_core_base_socket + diff --git a/xmake/core/sandbox/modules/io.lua b/xmake/core/sandbox/modules/io.lua index 1a02a77e61e..53ba52cc1cb 100644 --- a/xmake/core/sandbox/modules/io.lua +++ b/xmake/core/sandbox/modules/io.lua @@ -29,8 +29,6 @@ local vformat = require("sandbox/modules/vformat") local sandbox_io = sandbox_io or {} local sandbox_io_file = sandbox_io_file or {} local sandbox_io_filelock = sandbox_io_filelock or {} -sandbox_io._file = sandbox_io._file or io._file -sandbox_io._filelock = sandbox_io._filelock or io._filelock sandbox_io.lines = io.lines -- get file size diff --git a/xmake/core/sandbox/modules/ipairs.lua b/xmake/core/sandbox/modules/ipairs.lua index ee62dd87c89..1d936b2749a 100644 --- a/xmake/core/sandbox/modules/ipairs.lua +++ b/xmake/core/sandbox/modules/ipairs.lua @@ -21,45 +21,22 @@ -- load modules local table = require("base/table") --- ipairs --- --- e.g. --- --- @code --- --- for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}) do --- print("%d %s", idx, val) --- end --- --- for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}, function (v) return v:upper() end) do --- print("%d %s", idx, val) --- end --- --- for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}, function (v, a, b) return v:upper() .. a .. b end, "a", "b") do --- print("%d %s", idx, val) --- end --- --- @endcode -function sandbox_ipairs(t, filter, ...) +-- improve ipairs, wrap nil and single value +function sandbox_ipairs(t) - -- has filter? - local has_filter = type(filter) == "function" + -- exists the custom ipairs? + if type(t) == "table" and t.ipairs then + return t:ipairs() + end - -- init iterator - local args = table.pack(...) - local iter = function (t, i) + -- wrap table and return iterator + return function (t, i) i = i + 1 local v = t[i] if v ~= nil then - if has_filter then - v = filter(v, table.unpack(args, 1, args.n)) - end return i, v end - end - - -- return iterator and initialized state - return iter, table.wrap(t), 0 + end, table.wrap(t), 0 end -- load module diff --git a/xmake/core/sandbox/modules/pairs.lua b/xmake/core/sandbox/modules/pairs.lua index 92f533fe2ed..3aec2ebfc61 100644 --- a/xmake/core/sandbox/modules/pairs.lua +++ b/xmake/core/sandbox/modules/pairs.lua @@ -21,45 +21,18 @@ -- load modules local table = require("base/table") --- pairs --- --- e.g. --- --- @code --- --- local t = {a = "a", b = "b", c = "c", d = "d", e = "e", f = "f"} --- --- for key, val in pairs(t) do --- print("%s: %s", key, val) --- end --- --- for key, val in pairs(t, function (v) return v:upper() end) do --- print("%s: %s", key, val) --- end --- --- for key, val in pairs(t, function (v, a, b) return v:upper() .. a .. b end, "a", "b") do --- print("%s: %s", key, val) --- end --- --- @endcode --- -function sandbox_pairs(t, filter, ...) - - -- has filter? - local has_filter = type(filter) == "function" +-- improve pairs, wrap nil/single value +function sandbox_pairs(t) - -- init iterator - local args = {...} - local iter = function (t, i) - local k, v = next(t, i) - if v and has_filter then - v = filter(v, unpack(args)) - end - return k, v + -- exists the custom ipairs? + if type(t) == "table" and t.pairs then + return t:pairs() end - -- return iterator and initialized state - return iter, table.wrap(t), nil + -- wrap table and return iterator + return function (t, i) + return next(t, i) + end, table.wrap(t), nil end -- load module