sokol-luajit/ffi/uv-ffi.lua

1073 lines
29 KiB
Lua
Raw Normal View History

local ffi = require 'ffi'
local cast = ffi.cast
local C = ffi.C
local UV = nil
print("OS:", ffi.os)
if ffi.os == 'Windows' then
UV = ffi.load('libuv')
ffi.cdef [[
typedef uint32_t ULONG;
typedef struct uv_buf_t {
ULONG len;
char* base;
} uv_buf_t;
]]
end
if ffi.os == "Linux" then
UV = ffi.load('./bin/linux/x64/libuv.so')
ffi.cdef [[
typedef struct uv_buf_t {
char* base;
size_t len;
} uv_buf_t;
]]
end
ffi.cdef [[
uv_buf_t uv_buf_init(const char* base, unsigned int len);
typedef enum {
UV_EOF = -4095
} uv_errno_t;
typedef enum {
UV_UNKNOWN_HANDLE = 0,
UV_ASYNC,
UV_CHECK,
UV_FS_EVENT,
UV_FS_POLL,
UV_HANDLE,
UV_IDLE,
UV_NAMED_PIPE,
UV_POLL,
UV_PREPARE,
UV_PROCESS,
UV_STREAM,
UV_TCP,
UV_TIMER,
UV_TTY,
UV_UDP,
UV_SIGNAL,
UV_FILE,
UV_HANDLE_TYPE_MAX
} uv_handle_type;
typedef enum {
UV_UNKNOWN_REQ = 0,
UV_REQ,
UV_CONNECT,
UV_WRITE,
UV_SHUTDOWN,
UV_UDP_SEND,
UV_FS,
UV_WORK,
UV_GETADDRINFO,
UV_GETNAMEINFO,
UV_REQ_TYPE_MAX,
} uv_req_type;
size_t uv_loop_size(void);
size_t uv_req_size(uv_req_type type);
size_t uv_handle_size(uv_handle_type type);
]]
ffi.cdef(
string.format(
[[
struct uv_loop_s {uint8_t _[%d];};
struct uv_connect_s {uint8_t _[%d];};
struct uv_write_s {uint8_t _[%d];};
struct uv_shutdown_s {uint8_t _[%d];};
struct uv_getaddrinfo_s {uint8_t _[%d];};
struct uv_tcp_s {uint8_t _[%d];};
struct uv_tty_s {uint8_t _[%d];};
struct uv_pipe_s {uint8_t _[%d];};
struct uv_timer_s {uint8_t _[%d];};
]],
tonumber(UV.uv_loop_size()),
tonumber(UV.uv_req_size(UV.UV_CONNECT)),
tonumber(UV.uv_req_size(UV.UV_WRITE)),
tonumber(UV.uv_req_size(UV.UV_SHUTDOWN)),
tonumber(UV.uv_req_size(UV.UV_GETADDRINFO)),
tonumber(UV.uv_handle_size(UV.UV_TCP)),
tonumber(UV.uv_handle_size(UV.UV_TTY)),
tonumber(UV.uv_handle_size(UV.UV_NAMED_PIPE)),
tonumber(UV.uv_handle_size(UV.UV_TIMER))
)
)
ffi.cdef [[
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_req_s uv_req_t;
typedef struct uv_write_s uv_write_t;
typedef struct uv_connect_s uv_connect_t;
typedef struct uv_shutdown_s uv_shutdown_t;
typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;
typedef struct uv_tty_s uv_tty_t;
typedef struct uv_pipe_s uv_pipe_t;
typedef struct uv_timer_s uv_timer_t;
typedef enum uv_run_mode_e {
UV_RUN_DEFAULT = 0,
UV_RUN_ONCE,
UV_RUN_NOWAIT
} uv_run_mode;
int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr);
int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr);
int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size);
int uv_inet_ntop(int af, const void* src, char* dst, size_t size);
int uv_inet_pton(int af, const char* src, void* dst);
const char* uv_err_name(int err);
const char* uv_strerror(int err);
]]
local function makeCallback(type)
local thread, isMain = coroutine.running()
if not thread or isMain then return end
local cb
cb =
cast(
type,
function(...)
-- print('oncall', ...)
cb:free()
assert(coroutine.resume(thread, ...))
end
)
return cb
end
local function uvGetError(status)
return ffi.string(UV.uv_err_name(status)) .. ': ' .. ffi.string(UV.uv_strerror(status))
end
local function uvCheck(status)
if status < 0 then
error(uvGetError(status))
else
return status
end
end
local Loop = {}
-------------------------------------------------------------------------------
-- File
-------------------------------------------------------------------------------
local File = {}
ffi.cdef [[
typedef uint32_t DWORD;
typedef enum {
UV_FS_UNKNOWN = -1,
UV_FS_CUSTOM,
UV_FS_OPEN,
UV_FS_CLOSE,
UV_FS_READ,
UV_FS_WRITE,
UV_FS_SENDFILE,
UV_FS_STAT,
UV_FS_LSTAT,
UV_FS_FSTAT,
UV_FS_FTRUNCATE,
UV_FS_UTIME,
UV_FS_FUTIME,
UV_FS_ACCESS,
UV_FS_CHMOD,
UV_FS_FCHMOD,
UV_FS_FSYNC,
UV_FS_FDATASYNC,
UV_FS_UNLINK,
UV_FS_RMDIR,
UV_FS_MKDIR,
UV_FS_MKDTEMP,
UV_FS_RENAME,
UV_FS_SCANDIR,
UV_FS_LINK,
UV_FS_SYMLINK,
UV_FS_READLINK,
UV_FS_CHOWN,
UV_FS_FCHOWN,
UV_FS_REALPATH,
UV_FS_COPYFILE,
UV_FS_LCHOWN,
UV_FS_OPENDIR,
UV_FS_READDIR,
UV_FS_CLOSEDIR,
UV_FS_MKSTEMP
} uv_fs_type;
typedef struct {
long tv_sec;
long tv_nsec;
} uv_timespec_t;
struct uv__work {
void (*work)(struct uv__work *w);
void (*done)(struct uv__work *w, int status);
struct uv_loop_s* loop;
void* wq[2];
};
typedef struct {
uint64_t st_dev;
uint64_t st_mode;
uint64_t st_nlink;
uint64_t st_uid;
uint64_t st_gid;
uint64_t st_rdev;
uint64_t st_ino;
uint64_t st_size;
uint64_t st_blksize;
uint64_t st_blocks;
uint64_t st_flags;
uint64_t st_gen;
uv_timespec_t st_atim;
uv_timespec_t st_mtim;
uv_timespec_t st_ctim;
uv_timespec_t st_birthtim;
} uv_stat_t;
typedef enum {
UV_DIRENT_UNKNOWN,
UV_DIRENT_FILE,
UV_DIRENT_DIR,
UV_DIRENT_LINK,
UV_DIRENT_FIFO,
UV_DIRENT_SOCKET,
UV_DIRENT_CHAR,
UV_DIRENT_BLOCK
} uv_dirent_type_t;
typedef struct uv_dirent_s {
const char* name;
uv_dirent_type_t type;
} uv_dirent_t;
typedef struct uv_statfs_s {
uint64_t f_type;
uint64_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
uint64_t f_spare[4];
} uv_statfs_t;
typedef struct uv_dir_s {
uv_dirent_t* dirents;
size_t nentries;
} uv_dir_t;
typedef struct uv_fs_s uv_fs_t;
typedef void (*uv_fs_cb)(uv_fs_t* req);
/* uv_fs_t is a subclass of uv_req_t. */
typedef struct uv_fs_s {
void* data;
uv_req_type type;
void* reserved[6];
void* data;
uv_req_type type;
void* reserved[6];
uv_loop_t* loop;
uv_fs_cb cb;
size_t result;
void* ptr;
const char* path;
uv_stat_t statbuf; /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */
struct uv__work work_req;
int flags;
DWORD sys_errno_;
union {
uint16_t* pathw;
int fd;
} file;
union {
struct {
int mode;
uint16_t* new_pathw;
int file_flags;
int fd_out;
unsigned int nbufs;
uv_buf_t* bufs;
int64_t offset;
uv_buf_t bufsml[4];
} info;
struct {
double atime;
double mtime;
} time;
} fs;
};
int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
const char* uv_fs_get_path(const uv_fs_t* req);
int uv_exepath(char* buffer, size_t* size);
int uv_cwd(char* buffer, size_t* size);
]]
-------------------------------------------------------------------------------
-- Thread
-------------------------------------------------------------------------------
local Thread = {}
ffi.cdef [[
typedef uint32_t HANDLE;
typedef HANDLE uv_thread_t;
typedef void (*uv_thread_cb)(void* arg);
int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
typedef enum {
UV_THREAD_NO_FLAGS = 0x00,
UV_THREAD_HAS_STACK_SIZE = 0x01
} uv_thread_create_flags;
struct uv_thread_options_s {
unsigned int flags;
size_t stack_size;
/* More fields may be added at any time. */
};
typedef struct uv_thread_options_s uv_thread_options_t;
int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params,
uv_thread_cb entry, void* arg);
uv_thread_t uv_thread_self(void);
int uv_thread_join(uv_thread_t *tid);
int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
]]
-------------------------------------------------------------------------------
-- Req
-------------------------------------------------------------------------------
ffi.cdef [[
int uv_cancel(uv_req_t* req);
uv_req_type uv_req_get_type(const uv_req_t* req);
const char* uv_req_type_name(uv_req_type type);
]]
local Req = {}
function Req:cancel()
return uvCheck(UV.uv_cancel(cast('uv_req_t*', self)))
end
function Req:getType()
local id = UV.uv_req_get_type(cast('uv_req_t*', self))
return ffi.string(UV.uv_req_type_name(id))
end
-------------------------------------------------------------------------------
-- Connect
-------------------------------------------------------------------------------
local Connect = setmetatable({}, {__index = Req})
Connect.type = ffi.typeof 'uv_connect_t'
ffi.metatype(Connect.type, {__index = Connect})
function Connect.new()
return Connect.type()
end
-------------------------------------------------------------------------------
-- Write
-------------------------------------------------------------------------------
local Write = setmetatable({}, {__index = Req})
Write.type = ffi.typeof 'uv_write_t'
ffi.metatype(Write.type, {__index = Write})
function Write.new()
return Write.type()
end
-------------------------------------------------------------------------------
-- Shutdown
-------------------------------------------------------------------------------
local Shutdown = setmetatable({}, {__index = Req})
Shutdown.type = ffi.typeof 'uv_shutdown_t'
ffi.metatype(Shutdown.type, {__index = Shutdown})
function Shutdown.new()
return Shutdown.type()
end
-------------------------------------------------------------------------------
-- Getaddrinfo
-------------------------------------------------------------------------------
ffi.cdef [[
enum {
AF_UNSPEC = 0,
AF_INET = 2,
AF_INET6 = 10
};
enum {
SOCK_STREAM = 1,
SOCK_DGRAM = 2
};
typedef int32_t socklen_t;
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res);
int uv_getaddrinfo(
uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb,
const char* node, const char* service, const struct addrinfo* hints
);
void uv_freeaddrinfo(struct addrinfo* ai);
]]
local Getaddrinfo = setmetatable({}, {__index = Req})
Getaddrinfo.type = ffi.typeof 'uv_getaddrinfo_t'
ffi.metatype(Getaddrinfo.type, {__index = Getaddrinfo})
function Getaddrinfo.new()
return Getaddrinfo.type()
end
local families = {
inet = UV.AF_INET,
inet6 = UV.AF_INET6,
[UV.AF_INET] = 'inet',
[UV.AF_INET6] = 'inet6'
}
local socktypes = {
stream = UV.SOCK_STREAM,
dgram = UV.SOCK_DGRAM,
[UV.SOCK_STREAM] = 'stream',
[UV.SOCK_DGRAM] = 'dgram'
}
function Loop:getaddrinfo(node, service, hints)
local req = Getaddrinfo.new()
p(req)
local opts = ffi.new('struct addrinfo')
if type(service) == 'number' then
service = tostring(service)
end
if hints then
if hints.family then
opts.ai_family = assert(families[hints.family], 'Unknown family name')
end
if hints.socktype then
opts.ai_socktype = assert(socktypes[hints.socktype], 'Unknown socktype name')
end
end
p(node, service, hints)
uvCheck(UV.uv_getaddrinfo(self, req, makeCallback 'uv_getaddrinfo_cb', node, service, opts))
local _, status, res = coroutine.yield()
uvCheck(status)
local results = {}
local i = 1
while res ~= nil do
local family = families[res.ai_family]
local socktype = socktypes[res.ai_socktype]
local entry = {
family = family,
socktype = socktype
}
results[i] = entry
i = i + 1
if family == 'inet' then
local buf = ffi.new 'char[16]'
local addr = cast('const struct sockaddr_in*', res.ai_addr)
uvCheck(UV.uv_ip4_name(addr, buf, 16))
entry.addr = ffi.string(buf)
entry.port = C.ntohs(addr.sin_port)
elseif family == 'inet6' then
local buf = ffi.new 'char[46]'
local addr = cast('const struct sockaddr_in6*', res.ai_addr)
uvCheck(UV.uv_ip6_name(addr, buf, 46))
entry.addr = ffi.string(buf)
entry.port = C.ntohs(addr.sin6_port)
end
res = res.ai_next
end
return results
end
-------------------------------------------------------------------------------
-- Handle
-------------------------------------------------------------------------------
ffi.cdef [[
typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
typedef void (*uv_close_cb)(uv_handle_t* handle);
typedef void (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
struct in_addr {
unsigned long s_addr;
};
struct sockaddr_in {
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct in6_addr {
unsigned char s6_addr[16];
};
struct sockaddr_in6 {
uint16_t sin6_family;
uint16_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
typedef uint16_t sa_family_t;
struct sockaddr_storage {
sa_family_t ss_family;
char __ss_pad1[6];
int64_t __ss_align;
char __ss_pad2[112];
};
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
void *malloc(size_t size);
void free(void *ptr);
typedef int uv_file;
uv_handle_type uv_guess_handle(uv_file file);
int uv_is_active(const uv_handle_t* handle);
int uv_is_closing(const uv_handle_t* handle);
void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
void uv_ref(uv_handle_t* handle);
void uv_unref(uv_handle_t* handle);
int uv_has_ref(const uv_handle_t* handle);
int uv_send_buffer_size(uv_handle_t* handle, int* value);
int uv_recv_buffer_size(uv_handle_t* handle, int* value);
uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle);
void* uv_handle_get_data(const uv_handle_t* handle);
void* uv_handle_set_data(uv_handle_t* handle, void* data);
uv_handle_type uv_handle_get_type(const uv_handle_t* handle);
const char* uv_handle_type_name(uv_handle_type type);
]]
local function onAlloc(handle, suggestedSize, buf)
local cached = cast('uv_buf_t*', UV.uv_handle_get_data(handle))
if cached ~= nil then
buf.base = cached.base
buf.len = cached.len
else
local base = C.malloc(suggestedSize)
buf.base = base
buf.len = suggestedSize
-- Store the data in handle->data as a uv_buf_t*
local data = cast('uv_buf_t*', C.malloc(ffi.sizeof 'uv_buf_t'))
data.base = base
data.len = suggestedSize
UV.uv_handle_set_data(handle, data)
end
end
local allocCb = cast('uv_alloc_cb', onAlloc)
function Loop.guessHandle(fd)
return ffi.string(UV.uv_handle_type_name(UV.uv_guess_handle(fd)))
end
local Handle = {}
function Handle:isActive()
return UV.uv_is_active(cast('uv_handle_t*', self)) ~= 0
end
function Handle:isClosing()
return UV.uv_is_closing(cast('uv_handle_t*', self)) ~= 0
end
function Handle:close()
local handle = cast('uv_handle_t*', self)
local _, main = coroutine.running()
if main then
UV.uv_close(handle, nil)
else
UV.uv_close(handle, makeCallback 'uv_close_cb')
coroutine.yield()
end
local cached = cast('uv_buf_t*', UV.uv_handle_get_data(handle))
if cached ~= nil then
C.free(cached.base)
C.free(cached)
end
end
function Handle:ref()
return UV.uv_ref(cast('uv_handle_t*', self))
end
function Handle:unref()
return UV.uv_unref(cast('uv_handle_t*', self))
end
function Handle:hasRef()
return UV.uv_has_ref(cast('uv_handle_t*', self)) ~= 0
end
function Handle:setSendBufferSize(value)
uvCheck(UV.uv_send_buffer_size(cast('uv_handle_t*', self), value))
end
function Handle:getSendBufferSize()
local out = ffi.new('int[1]')
uvCheck(UV.uv_send_buffer_size(cast('uv_handle_t*', self), out))
return out[0]
end
function Handle:setRecvBufferSize(value)
uvCheck(UV.uv_recv_buffer_size(cast('uv_handle_t*', self), value))
end
function Handle:getRecvBufferSize()
local out = ffi.new('int[1]')
uvCheck(UV.uv_recv_buffer_size(cast('uv_handle_t*', self), out))
return out[0]
end
function Handle:getLoop()
return UV.uv_handle_get_loop(cast('uv_handle_t*', self))
end
function Handle:getType()
local id = UV.uv_handle_get_type(cast('uv_handle_t*', self))
return ffi.string(UV.uv_handle_type_name(id))
end
-------------------------------------------------------------------------------
-- Stream
-------------------------------------------------------------------------------
ffi.cdef [[
typedef struct sws_handle_req_t{
struct uv_tcp_s handle;
int id;
struct sws_parse_req_t parse_req;
} sws_handle_req_t;
typedef void (*uv_read_cb)(uv_stream_t* stream, int64_t nread, const uv_buf_t* buf);
typedef void (*uv_write_cb)(uv_write_t* req, int status);
typedef void (*uv_connect_cb)(uv_connect_t* req, int status);
typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status);
typedef void (*uv_connection_cb)(uv_stream_t* server, int status);
int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb);
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
int uv_accept(uv_stream_t* server, uv_stream_t* client);
int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
int uv_read_stop(uv_stream_t*);
int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
uv_stream_t* send_handle, uv_write_cb cb);
int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs);
int uv_is_readable(const uv_stream_t* handle);
int uv_is_writable(const uv_stream_t* handle);
int uv_stream_set_blocking(uv_stream_t* handle, int blocking);
size_t uv_stream_get_write_queue_size(const uv_stream_t* stream);
]]
local Stream = setmetatable({}, {__index = Handle})
function Stream:shutdown()
local req = Shutdown.new()
uvCheck(UV.uv_shutdown(req, cast('uv_stream_t*', self), makeCallback 'uv_shutdown_cb'))
local _, status = coroutine.yield()
uvCheck(status)
end
function Stream:listen(backlog, onConnection)
local cb = cast('uv_connection_cb', onConnection)
uvCheck(UV.uv_listen(cast('uv_stream_t*', self), backlog, cb))
return cb
end
function Stream:accept(client)
uvCheck(UV.uv_accept(cast('uv_stream_t*', self), cast('uv_stream_t*', client)))
end
function Stream:readStart(onRead)
local function onEvent(_, status, buf)
print('onRead', _, status, buf)
if status == 0 then
return
end
if status == UV.UV_EOF then
onRead(nil)
elseif status < 0 then
return onRead(uvGetError(status))
else
onRead(nil, ffi.string(buf.base, status))
end
end
local cb = cast('uv_read_cb', onEvent)
uvCheck(UV.uv_read_start(cast('uv_stream_t*', self), allocCb, cb))
end
function Stream:readStop()
uvCheck(UV.uv_read_stop(cast('uv_stream_t*', self)))
end
function Stream:write(data)
local req = Write.new()
local bufs = ffi.new('uv_buf_t[1]')
bufs[0].base = cast('char*', data)
bufs[0].len = #data
local cb = makeCallback 'uv_write_cb'
uvCheck(UV.uv_write(req, cast('uv_stream_t*', self), bufs, 1, cb))
if cb then
local _, status = coroutine.yield()
uvCheck(status)
end
end
function Stream:write2(handle)
local req = Write.new()
local cb = makeCallback 'uv_write_cb'
uvCheck(UV.uv_write(req, cast('uv_stream_t*', self), nil, 0, cast('uv_stream_t*', handle), cb))
local _, status = coroutine.yield()
uvCheck(status)
end
function Stream:tryWrite(data)
local bufs = ffi.new('uv_buf_t[1]')
bufs[0].base = cast('char*', data)
bufs[0].len = #data
return uvCheck(UV.uv_try_write(cast('uv_stream_t*', self), bufs, 1))
end
function Stream:isReadable()
return UV.uv_is_readable(cast('uv_stream_t*', self)) ~= 0
end
function Stream:isWritable()
return UV.uv_is_writable(cast('uv_stream_t*', self)) ~= 0
end
function Stream:setBlocking(blocking)
uvCheck(UV.uv_set_blocking(cast('uv_stream_t*', self), blocking))
end
function Stream:getWriteQueueSize()
return tonumber(UV.uv_stream_get_write_queue_size(cast('uv_stream_t*', self)))
end
-------------------------------------------------------------------------------
-- Tcp
-------------------------------------------------------------------------------
ffi.cdef [[
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle);
int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags);
int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr_in* addr, uv_connect_cb cb);
]]
local Tcp = setmetatable({}, {__index = Stream})
Tcp.type = ffi.typeof 'uv_tcp_t'
function Loop:newTcp()
local tcp = Tcp.type()
uvCheck(UV.uv_tcp_init(self, tcp))
return tcp
end
function Tcp:getsockname()
-- TODO: Improve output
return UV.uv_tcp_getsockname(self)
end
function Tcp:getpeername()
-- TODO: Improve output
return UV.uv_tcp_getpeername(self)
end
function Tcp:connect(host, port)
local req = Connect.new()
local addr = ffi.new("struct sockaddr_in [1]")
UV.uv_ip4_addr(host, port, cast("struct sockaddr_in *", addr))
uvCheck(UV.uv_tcp_connect(req, self, addr, makeCallback 'uv_connect_cb'))
local _, status = coroutine.yield()
uvCheck(status)
end
ffi.metatype(Tcp.type, {__index = Tcp})
-------------------------------------------------------------------------------
-- Tty
-------------------------------------------------------------------------------
ffi.cdef [[
typedef enum {
/* Initial/normal terminal mode */
UV_TTY_MODE_NORMAL,
/* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
UV_TTY_MODE_RAW,
/* Binary-safe I/O mode for IPC (Unix-only) */
UV_TTY_MODE_IO
} uv_tty_mode_t;
int uv_tty_init(uv_loop_t* loop, uv_tty_t* handle, uv_file fd, int unused);
int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode);
int uv_tty_reset_mode(void);
int uv_tty_get_winsize(uv_tty_t* handle, int* width, int* height);
]]
local Tty = setmetatable({}, {__index = Stream})
Tty.type = ffi.typeof 'uv_tty_t'
function Loop:newTty(fd)
local tty = Tty.type()
uvCheck(UV.uv_tty_init(self, tty, fd, 0))
return tty
end
function Tty:setMode(mode)
uvCheck(
UV.uv_tty_set_mode(self, assert(UV['UV_TTY_MODE_' .. string.upper(mode)], 'Unknown tty mode'))
)
end
function Tty:resetMode()
uvCheck(UV.uv_tty_reset_mode())
end
function Tty:getWinsize()
local width = ffi.new('int[1]')
local height = ffi.new('int[1]')
uvCheck(UV.uv_tty_get_winsize(self, width, height))
return tonumber(width[0]), tonumber(height[0])
end
ffi.metatype(Tty.type, {__index = Tty})
-------------------------------------------------------------------------------
-- Pipe
-------------------------------------------------------------------------------
ffi.cdef [[
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc);
int uv_pipe_open(uv_pipe_t* handle, uv_file file);
int uv_pipe_bind(uv_pipe_t* handle, const char* name);
void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb);
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size);
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size);
void uv_pipe_pending_instances(uv_pipe_t* handle, int count);
int uv_pipe_pending_count(uv_pipe_t* handle);
uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle);
int uv_pipe_chmod(uv_pipe_t* handle, int flags);
]]
local Pipe = setmetatable({}, {__index = Stream})
Pipe.type = ffi.typeof 'uv_pipe_t'
function Loop:newPipe(ipc)
local pipe = Pipe.type()
uvCheck(UV.uv_pipe_init(self, pipe, ipc and 1 or 0))
return pipe
end
function Pipe:open(fd)
uvCheck(UV.uv_pipe_open(self, fd))
end
function Pipe:bind(path)
uvCheck(UV.uv_pipe_bind(self, path))
end
-- TODO: add more pipe functions
ffi.metatype(Pipe.type, {__index = Pipe})
-------------------------------------------------------------------------------
-- Timer
-------------------------------------------------------------------------------
ffi.cdef [[
typedef void (*uv_timer_cb)(uv_timer_t* handle);
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle);
int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat);
int uv_timer_stop(uv_timer_t* handle);
int uv_timer_again(uv_timer_t* handle);
void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat);
uint64_t uv_timer_get_repeat(const uv_timer_t* handle);
]]
local Timer = setmetatable({}, {__index = Handle})
Timer.Type = ffi.typeof 'uv_timer_t'
function Loop:newTimer()
local timer = Timer.Type()
uvCheck(UV.uv_timer_init(self, timer))
return timer
end
function Timer:sleep(timeout)
uvCheck(UV.uv_timer_start(self, makeCallback 'uv_timer_cb', timeout, 0))
coroutine.yield()
uvCheck(UV.uv_timer_stop(self))
end
function Timer:start(callback, timeout, rep)
local cb = cast('uv_timer_cb', timeout)
uvCheck(UV.uv_timer_start(self, callback, cb, rep))
return cb
end
function Timer:stop()
uvCheck(UV.uv_timer_stop(self))
end
function Timer:again()
uvCheck(UV.uv_timer_again(self))
end
function Timer:setRepeat(rep)
uvCheck(UV.uv_timer_set_repeat(self, rep))
end
function Timer:getRepeat()
return UV.uv_timer_get_repeat(self)
end
local function onGc(handle)
if not handle:isClosing() then
-- We can't safely close handles here because onClose happens after the
-- struct is freed by lua.
-- Instead abort the process so the programmer can fix it early.
handle = cast('uv_' .. Handle.getType(handle) .. '_t*', handle)
error('Unclosed ' .. tostring(handle) .. ' got garbage collected')
end
end
ffi.metatype(Timer.Type, {__index = Timer, __gc = onGc})
-------------------------------------------------------------------------------
-- Loop
-------------------------------------------------------------------------------
ffi.cdef [[
uv_loop_t* uv_default_loop();
int uv_loop_init(uv_loop_t* loop);
int uv_loop_close(uv_loop_t* loop);
int uv_loop_alive(const uv_loop_t* loop);
void uv_stop(uv_loop_t* loop);
uint64_t uv_now(const uv_loop_t* loop);
void uv_update_time(uv_loop_t* loop);
void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);
int uv_run(uv_loop_t* loop, uv_run_mode mode);
]]
local LoopType = ffi.typeof 'uv_loop_t'
function Loop.new()
local loop = LoopType()
uvCheck(UV.uv_loop_init(loop))
return loop
end
function Loop:close()
uvCheck(UV.uv_loop_close(self))
end
function Loop:alive()
return UV.uv_loop_alive(self) ~= 0
end
function Loop:stop()
return UV.uv_loop_stop(self)
end
function Loop:now()
return UV.uv_loop_now(self)
end
function Loop:updateTime()
return UV.uv_update_time(self)
end
function Loop:walk(callback)
local function onHandle(handle)
callback(cast('uv_' .. Handle.getType(handle) .. '_t*', handle))
end
local cb = cast('uv_walk_cb', onHandle)
UV.uv_walk(self, cb, nil)
cb:free()
end
function Loop:run(mode)
mode = assert(UV['UV_RUN_' .. string.upper(mode)], 'Unknown run mode')
return tonumber(uvCheck(UV.uv_run(self, mode)))
end
function Loop:bind(server, host, port)
local addr = ffi.new("struct sockaddr_in [1]")
local res = UV.uv_ip4_addr(host, port, cast("struct sockaddr_in *", addr))
uvCheck(UV.uv_tcp_bind(server, cast("struct sockaddr *", addr), 0 ))
end
function Loop:listen(tcp, backlog, onConnect)
tcp:listen( backlog, onConnect )
end
ffi.metatype(LoopType, {__index = Loop})
-------------------------------------------------------------------------------
Loop.Req = Req
Loop.Connect = Connect
Loop.Write = Write
Loop.Shutdown = Shutdown
Loop.Handle = Handle
Loop.Stream = Stream
Loop.Tcp = Tcp
Loop.Timer = Timer
Loop.uv = UV
return UV.uv_default_loop()