summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2019-11-04 22:20:02 +0800
committerGitHub <[email protected]>2019-11-04 22:20:02 +0800
commitb1eee1ec65dd0f9d3a26af52596d86c331160587 (patch)
tree6811a354f07c146ba4417e649d3a31d0c9aabd45
parent20b40c57bbe9d28f822d5feac304a5b892b02790 (diff)
parentf00316a42943a9a75303658f8d646d35f9e8d1b9 (diff)
Merge pull request #593 from xmake-io/socket
Add io.socket support
-rw-r--r--core/src/demo/xmake.lua6
-rw-r--r--core/src/xmake/io/file_close.c4
-rw-r--r--core/src/xmake/io/file_flush.c4
-rw-r--r--core/src/xmake/io/file_isatty.c2
-rw-r--r--core/src/xmake/io/file_open.c8
-rw-r--r--core/src/xmake/io/file_rawfd.c4
-rw-r--r--core/src/xmake/io/file_read.c36
-rw-r--r--core/src/xmake/io/file_seek.c8
-rw-r--r--core/src/xmake/io/file_size.c4
-rw-r--r--core/src/xmake/io/file_write.c2
-rw-r--r--core/src/xmake/io/prefix.h4
-rw-r--r--core/src/xmake/io/socket_accept.c57
-rw-r--r--core/src/xmake/io/socket_bind.c73
-rw-r--r--core/src/xmake/io/socket_close.c57
-rw-r--r--core/src/xmake/io/socket_connect.c73
-rw-r--r--core/src/xmake/io/socket_listen.c58
-rw-r--r--core/src/xmake/io/socket_open.c70
-rw-r--r--core/src/xmake/io/socket_rawfd.c62
-rw-r--r--core/src/xmake/io/socket_recv.c77
-rw-r--r--core/src/xmake/io/socket_recvfrom.c101
-rw-r--r--core/src/xmake/io/socket_send.c109
-rw-r--r--core/src/xmake/io/socket_sendfile.c117
-rw-r--r--core/src/xmake/io/socket_sendto.c106
-rw-r--r--core/src/xmake/io/socket_wait.c61
-rw-r--r--core/src/xmake/io/stdfile.c2
-rw-r--r--core/src/xmake/machine.c28
-rw-r--r--core/src/xmake/makefile13
-rw-r--r--tests/modules/bytes/test.lua37
-rw-r--r--tests/modules/socket/tcp/echo_client.lua21
-rw-r--r--tests/modules/socket/tcp/echo_server.lua32
-rw-r--r--tests/modules/socket/tcp/file_client.lua35
-rw-r--r--tests/modules/socket/tcp/file_server.lua24
-rw-r--r--tests/modules/socket/udp/echo_client.lua15
-rw-r--r--tests/modules/socket/udp/echo_server.lua18
-rw-r--r--xmake/core/base/bytes.lua441
-rw-r--r--xmake/core/base/io.lua180
-rw-r--r--xmake/core/base/socket.lua581
-rw-r--r--xmake/core/sandbox/modules/bytes.lua23
-rw-r--r--xmake/core/sandbox/modules/import/core/base/socket.lua217
-rw-r--r--xmake/core/sandbox/modules/io.lua2
-rw-r--r--xmake/core/sandbox/modules/ipairs.lua41
-rw-r--r--xmake/core/sandbox/modules/pairs.lua45
42 files changed, 2695 insertions, 163 deletions
diff --git a/core/src/demo/xmake.lua b/core/src/demo/xmake.lua
index edd6adfdd..d0ac78909 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 6b3efdd98..1e3ebd131 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 72969b09d..a4bf0f672 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 d8c5183d8..06eea4b29 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 46e63cd62..9db81a82c 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 dcc69eceb..a298c29c2 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 438dc022d..d5748ffe3 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 a4e14bffa..67cfe061c 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 4e502bbfb..637268ced 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 43aaf606e..b276a0e10 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 ba567f6fb..c96570396 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 000000000..efd667c5a
--- /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 000000000..f4c1c06b9
--- /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 000000000..fe0d24f32
--- /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 000000000..d758a9734
--- /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 000000000..c075bcb67
--- /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 000000000..fbfd7a3c7
--- /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 000000000..8682e99fc
--- /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 000000000..4ed41e080
--- /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 000000000..2933a44f0
--- /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 000000000..8cce64f29
--- /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 000000000..df29afd32
--- /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 000000000..6b1f8fa49
--- /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 000000000..d9b845785
--- /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 c5adae143..8f466fa7a 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 b98e60236..835e10b73 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 996123191..e2660128a 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 000000000..52b754c0a
--- /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 000000000..f3d6d9bae
--- /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 000000000..9ff19dbed
--- /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 000000000..42532f5fe
--- /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 000000000..d619a496f
--- /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 000000000..f46ae4716
--- /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 000000000..84f55eae4
--- /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 000000000..6c62697aa
--- /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 "<bytes(" .. self:size() .. "): " .. table.concat(parts, " ") .. (self:size() > 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 "<bytes>"
+ end
+})
+
+-- return module: bytes
+return bytes
diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua
index f6c931970..6dc13ca30 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 "<file: " .. str .. ">"
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 "<filelock: " .. str .. ">"
end
-- gc(filelock)
diff --git a/xmake/core/base/socket.lua b/xmake/core/base/socket.lua
new file mode 100644
index 000000000..4a2dfdcdc
--- /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("<socket: %s%s/%s>", 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 000000000..c1cbbcb2a
--- /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 000000000..73aa0994b
--- /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 1a02a77e6..53ba52cc1 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 ee62dd87c..1d936b274 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 92f533fe2..3aec2ebfc 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