Scripting: add socket bindings

This commit is contained in:
Adam Higerd 2022-09-12 11:50:54 -05:00 committed by Vicki Pfau
parent a11b103a9c
commit d852c7c8f0
7 changed files with 479 additions and 3 deletions

View File

@ -10,15 +10,12 @@
CXX_GUARD_START
#include <mgba/core/log.h>
#ifdef USE_DEBUGGERS
#include <mgba/debugger/debugger.h>
#endif
#include <mgba/script/macros.h>
#include <mgba/script/types.h>
mLOG_DECLARE_CATEGORY(SCRIPT);
struct mCore;
struct mScriptTextBuffer;
mSCRIPT_DECLARE_STRUCT(mCore);

View File

@ -0,0 +1,25 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef M_SCRIPT_SOCKET_H
#define M_SCRIPT_SOCKET_H
enum mSocketErrorCode {
mSCRIPT_SOCKERR_UNKNOWN_ERROR = -1,
mSCRIPT_SOCKERR_OK = 0,
mSCRIPT_SOCKERR_AGAIN,
mSCRIPT_SOCKERR_ADDRESS_IN_USE,
mSCRIPT_SOCKERR_CONNECTION_REFUSED,
mSCRIPT_SOCKERR_DENIED,
mSCRIPT_SOCKERR_FAILED,
mSCRIPT_SOCKERR_NETWORK_UNREACHABLE,
mSCRIPT_SOCKERR_NOT_FOUND,
mSCRIPT_SOCKERR_NO_DATA,
mSCRIPT_SOCKERR_OUT_OF_MEMORY,
mSCRIPT_SOCKERR_TIMEOUT,
mSCRIPT_SOCKERR_UNSUPPORTED,
};
#endif

View File

@ -10,6 +10,7 @@
CXX_GUARD_START
#include <mgba/core/log.h>
#include <mgba/script/types.h>
#include <mgba-util/table.h>
#include <mgba-util/vfs.h>
@ -18,6 +19,8 @@ CXX_GUARD_START
#define mSCRIPT_CONSTANT_PAIR(NS, CONST) { #CONST, mScriptValueCreateFromSInt(NS ## _ ## CONST) }
#define mSCRIPT_KV_SENTINEL { NULL, NULL }
mLOG_DECLARE_CATEGORY(SCRIPT);
struct mScriptFrame;
struct mScriptFunction;
struct mScriptEngineContext;
@ -83,6 +86,7 @@ struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
void mScriptContextAttachStdlib(struct mScriptContext* context);
void mScriptContextAttachSocket(struct mScriptContext* context);
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants);
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);

View File

@ -113,6 +113,7 @@ void ScriptingController::runCode(const QString& code) {
void ScriptingController::init() {
mScriptContextInit(&m_scriptContext);
mScriptContextAttachStdlib(&m_scriptContext);
mScriptContextAttachSocket(&m_scriptContext);
mScriptContextRegisterEngines(&m_scriptContext);
mScriptContextAttachLogger(&m_scriptContext, &m_logger);

View File

@ -1,6 +1,7 @@
include(ExportDirectory)
set(SOURCE_FILES
context.c
socket.c
stdlib.c
types.c)

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/script/lua.h>
#include <mgba/internal/script/socket.h>
#include <mgba/script/context.h>
#include <mgba/script/macros.h>
#include <mgba-util/string.h>
@ -51,6 +53,156 @@ static int _luaLenList(lua_State* lua);
static int _luaRequireShim(lua_State* lua);
static const char* _socketLuaSource =
"socket = {\n"
" ERRORS = {},\n"
" tcp = function() return socket._create(_socket.create(), socket._tcpMT) end,\n"
" bind = function(address, port)\n"
" local s = socket.tcp()\n"
" local ok, err = s:bind(address, port)\n"
" if ok then return s end\n"
" return ok, err\n"
" end,\n"
" connect = function(address, port)\n"
" local s = socket.tcp()\n"
" local ok, err = s:connect(address, port)\n"
" if ok then return s end\n"
" return ok, err\n"
" end,\n"
" _create = function(sock, mt) return setmetatable({\n"
" _s = sock,\n"
" _callbacks = {},\n"
" _nextCallback = 1,\n"
" }, mt) end,\n"
" _wrap = function(status)\n"
" if status == 0 then return 1 end\n"
" return nil, socket.ERRORS[status] or ('error#' .. status)\n"
" end,\n"
" _mt = {\n"
" __index = {\n"
" close = function(self)\n"
" if self._onframecb then\n"
" callbacks:remove(self._onframecb)\n"
" self._onframecb = nil\n"
" end\n"
" self._callbacks = {}\n"
" return self._s:close()\n"
" end,\n"
" add = function(self, event, callback)\n"
" if not self._callbacks[event] then self._callbacks[event] = {} end\n"
" local cbid = self._nextCallback\n"
" self._nextCallback = cbid + 1\n"
" self._callbacks[event][cbid] = callback\n"
" return id\n"
" end,\n"
" remove = function(self, cbid)\n"
" for _, group in pairs(self._callbacks) do\n"
" if group[cbid] then\n"
" group[cbid] = nil\n"
" end\n"
" end\n"
" end,\n"
" _dispatch = function(self, event, ...)\n"
" if not self._callbacks[event] then return end\n"
" for k, cb in pairs(self._callbacks[event]) do\n"
" if cb then\n"
" local ok, ret = pcall(cb, self, ...)\n"
" if not ok then console:error(ret) end\n"
" end\n"
" end\n"
" end,\n"
" },\n"
" },\n"
" _tcpMT = {\n"
" __index = {\n"
" _hook = function(self, status)\n"
" if status == 0 then\n"
" self._onframecb = callbacks:add('frame', function() self:poll() end)\n"
" end\n"
" return socket._wrap(status)\n"
" end,\n"
" bind = function(self, address, port)\n"
" return socket._wrap(self._s:open(address or '', port))\n"
" end,\n"
" connect = function(self, address, port)\n"
" local status = self._s:connect(address, port)\n"
" end,\n"
" listen = function(self, backlog)\n"
" local status = self._s:listen(backlog or 1)\n"
" return self:_hook(status)\n"
" end,\n"
" accept = function(self)\n"
" local client = self._s:accept()\n"
" if client.error ~= 0 then\n"
" client:close()\n"
" return socket._wrap(client.error)\n"
" end\n"
" local sock = socket._create(client, socket._tcpMT)\n"
" sock:_hook(0)\n"
" return sock\n"
" end,\n"
" send = function(self, data, i, j)\n"
" local result = self._s:send(string.sub(data, i or 1, j))\n"
" if result < 0 then return socket._wrap(self._s.error) end\n"
" if i then return result + i - 1 end\n"
" return result\n"
" end,\n"
// TODO: This does not match the API for LuaSocket's receive() implementation
" receive = function(self, maxBytes)\n"
" local result = self._s:recv(maxBytes)\n"
" if (not result or #result == 0) and self._s.error ~= 0 then\n"
" return socket._wrap(self._s.error)\n"
" elseif not result or #result == 0 then\n"
" return nil, 'disconnected'\n"
" end\n"
" return result or ''\n"
" end,\n"
" hasdata = function(self)\n"
" local status = self._s:select(0)\n"
" if status < 0 then\n"
" return socket._wrap(self._s.error)\n"
" end\n"
" return status > 0\n"
" end,\n"
" poll = function(self)\n"
" local status, err = self:hasdata()\n"
" if err then\n"
" self:_dispatch('error', err)\n"
" elseif status then\n"
" self:_dispatch('received')\n"
" end\n"
" end,\n"
" },\n"
" },\n"
" _errMT = {\n"
" __index = function (tbl, key)\n"
" return rawget(tbl, C.SOCKERR[key])\n"
" end,\n"
" },\n"
"}\n"
"setmetatable(socket._tcpMT.__index, socket._mt)\n"
"setmetatable(socket.ERRORS, socket._errMT)\n";
static const struct _mScriptSocketError {
enum mSocketErrorCode err;
const char* message;
} _mScriptSocketErrors[] = {
{ mSCRIPT_SOCKERR_UNKNOWN_ERROR, "unknown error" },
{ mSCRIPT_SOCKERR_OK, NULL },
{ mSCRIPT_SOCKERR_AGAIN, "temporary failure" },
{ mSCRIPT_SOCKERR_ADDRESS_IN_USE, "address in use" },
{ mSCRIPT_SOCKERR_DENIED, "access denied" },
{ mSCRIPT_SOCKERR_UNSUPPORTED, "unsupported" },
{ mSCRIPT_SOCKERR_CONNECTION_REFUSED, "connection refused" },
{ mSCRIPT_SOCKERR_NETWORK_UNREACHABLE, "network unreachable" },
{ mSCRIPT_SOCKERR_TIMEOUT, "timeout" },
{ mSCRIPT_SOCKERR_FAILED, "failed" },
{ mSCRIPT_SOCKERR_NOT_FOUND, "not found" },
{ mSCRIPT_SOCKERR_NO_DATA, "no data" },
{ mSCRIPT_SOCKERR_OUT_OF_MEMORY, "out of memory" },
};
static const int _mScriptSocketNumErrors = sizeof(_mScriptSocketErrors) / sizeof(struct _mScriptSocketError);
#if LUA_VERSION_NUM < 503
#define lua_pushinteger lua_pushnumber
#endif
@ -177,6 +329,26 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS
lua_getglobal(luaContext->lua, "require");
luaContext->require = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
int status = luaL_dostring(luaContext->lua, _socketLuaSource);
if (status) {
mLOG(SCRIPT, ERROR, "Error in dostring while initializing sockets: %s\n", lua_tostring(luaContext->lua, -1));
lua_pop(luaContext->lua, 1);
} else {
int i;
lua_getglobal(luaContext->lua, "socket");
lua_getfield(luaContext->lua, -1, "ERRORS");
for (i = 0; i < _mScriptSocketNumErrors; i++) {
struct _mScriptSocketError* err = &_mScriptSocketErrors[i];
if (err->message) {
lua_pushstring(luaContext->lua, err->message);
} else {
lua_pushnil(luaContext->lua);
}
lua_seti(luaContext->lua, -2, err->err);
}
lua_pop(luaContext->lua, 2);
}
return &luaContext->d;
}

276
src/script/socket.c Normal file
View File

@ -0,0 +1,276 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/script/context.h>
#include <mgba/internal/script/socket.h>
#include <mgba/script/macros.h>
#include <mgba-util/socket.h>
struct mScriptSocket {
Socket socket;
struct Address address;
int32_t error;
uint16_t port;
};
mSCRIPT_DECLARE_STRUCT(mScriptSocket);
static const struct _mScriptSocketErrorMapping {
int32_t nativeError;
enum mSocketErrorCode mappedError;
} _mScriptSocketErrorMappings[] = {
{ EAGAIN, mSCRIPT_SOCKERR_AGAIN },
{ EADDRINUSE, mSCRIPT_SOCKERR_ADDRESS_IN_USE },
{ ECONNREFUSED, mSCRIPT_SOCKERR_CONNECTION_REFUSED },
{ EACCES, mSCRIPT_SOCKERR_DENIED },
{ EPERM, mSCRIPT_SOCKERR_DENIED },
{ ENOTRECOVERABLE, mSCRIPT_SOCKERR_FAILED },
{ ENETUNREACH, mSCRIPT_SOCKERR_NETWORK_UNREACHABLE },
{ ETIMEDOUT, mSCRIPT_SOCKERR_TIMEOUT },
{ EINVAL, mSCRIPT_SOCKERR_UNSUPPORTED },
{ EPROTONOSUPPORT, mSCRIPT_SOCKERR_UNSUPPORTED },
#ifndef USE_GETHOSTBYNAME
#ifdef _WIN32
{ WSATRY_AGAIN, mSCRIPT_SOCKERR_AGAIN },
{ WSANO_RECOVERY, mSCRIPT_SOCKERR_FAILED },
{ WSANO_DATA, mSCRIPT_SOCKERR_NO_DATA },
{ WSAHOST_NOT_FOUND, mSCRIPT_SOCKERR_NOT_FOUND },
{ WSATYPE_NOT_FOUND, mSCRIPT_SOCKERR_NOT_FOUND },
{ WSA_NOT_ENOUGH_MEMORY, mSCRIPT_SOCKERR_OUT_OF_MEMORY },
{ WSAEAFNOSUPPORT, mSCRIPT_SOCKERR_UNSUPPORTED },
{ WSAEINVAL, mSCRIPT_SOCKERR_UNSUPPORTED },
{ WSAESOCKTNOSUPPORT, mSCRIPT_SOCKERR_UNSUPPORTED },
#else
{ EAI_AGAIN, mSCRIPT_SOCKERR_AGAIN },
{ EAI_FAIL, mSCRIPT_SOCKERR_FAILED },
{ EAI_NODATA, mSCRIPT_SOCKERR_NO_DATA },
{ EAI_NONAME, mSCRIPT_SOCKERR_NOT_FOUND },
{ EAI_MEMORY, mSCRIPT_SOCKERR_OUT_OF_MEMORY },
#endif
#else
{ -TRY_AGAIN, mSCRIPT_SOCKERR_AGAIN },
{ -NO_RECOVERY, mSCRIPT_SOCKERR_FAILED },
{ -NO_DATA, mSCRIPT_SOCKERR_NO_DATA },
{ -HOST_NOT_FOUND, mSCRIPT_SOCKERR_NOT_FOUND },
#endif
};
static const int _mScriptSocketNumErrorMappings = sizeof(_mScriptSocketErrorMappings) / sizeof(struct _mScriptSocketErrorMapping);
static void _mScriptSocketSetError(struct mScriptSocket* ssock, int32_t err) {
if (!err) {
ssock->error = mSCRIPT_SOCKERR_OK;
return;
}
int i;
for (i = 0; i < _mScriptSocketNumErrorMappings; i++) {
if (_mScriptSocketErrorMappings[i].nativeError == err) {
ssock->error = _mScriptSocketErrorMappings[i].mappedError;
return;
}
}
ssock->error = mSCRIPT_SOCKERR_UNKNOWN_ERROR;
}
static void _mScriptSocketSetSocket(struct mScriptSocket* ssock, Socket socket) {
if (SOCKET_FAILED(socket)) {
ssock->socket = INVALID_SOCKET;
_mScriptSocketSetError(ssock, SocketError());
} else {
ssock->socket = socket;
ssock->error = mSCRIPT_SOCKERR_OK;
}
}
struct mScriptValue* _mScriptSocketCreate() {
struct mScriptSocket client = {
.socket = INVALID_SOCKET,
.error = mSCRIPT_SOCKERR_OK,
.port = 0
};
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptSocket));
result->value.opaque = calloc(1, sizeof(struct mScriptSocket));
*(struct mScriptSocket*) result->value.opaque = client;
result->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
return result;
}
void _mScriptSocketClose(struct mScriptSocket* ssock) {
if (!SOCKET_FAILED(ssock->socket)) {
SocketClose(ssock->socket);
}
}
struct mScriptValue* _mScriptSocketAccept(struct mScriptSocket* ssock) {
struct mScriptValue* value = _mScriptSocketCreate();
struct mScriptSocket* client = (struct mScriptSocket*) value->value.opaque;
_mScriptSocketSetSocket(client, SocketAccept(ssock->socket, &client->address));
if (!client->error) {
SocketSetBlocking(client->socket, false);
}
return value;
}
int32_t _mScriptSocketOpen(struct mScriptSocket* ssock, const char* addressStr, uint16_t port) {
struct Address* addr = NULL;
if (addressStr && addressStr[0]) {
int32_t err = SocketResolveHost(addressStr, &ssock->address);
if (err) {
_mScriptSocketSetError(ssock, err);
return err;
}
addr = &ssock->address;
}
ssock->port = port;
_mScriptSocketSetSocket(ssock, SocketOpenTCP(port, addr));
if (!ssock->error) {
SocketSetBlocking(ssock->socket, false);
}
return ssock->error;
}
int32_t _mScriptSocketConnect(struct mScriptSocket* ssock, const char* addressStr, uint16_t port) {
int32_t err = SocketResolveHost(addressStr, &ssock->address);
if (err) {
_mScriptSocketSetError(ssock, err);
return err;
}
ssock->port = port;
_mScriptSocketSetSocket(ssock, SocketConnectTCP(port, &ssock->address));
if (!ssock->error) {
SocketSetBlocking(ssock->socket, false);
}
return ssock->error;
}
int32_t _mScriptSocketListen(struct mScriptSocket* ssock, uint32_t queueLength) {
_mScriptSocketSetError(ssock, SocketListen(ssock->socket, queueLength));
return ssock->error;
}
int32_t _mScriptSocketSend(struct mScriptSocket* ssock, struct mScriptString* data) {
ssize_t written = SocketSend(ssock->socket, data->buffer, data->size);
if (written < 0) {
_mScriptSocketSetError(ssock, SocketError());
return -ssock->error;
}
ssock->error = mSCRIPT_SOCKERR_OK;
return written;
}
struct mScriptValue* _mScriptSocketRecv(struct mScriptSocket* ssock, uint32_t maxBytes) {
struct mScriptValue* value = mScriptStringCreateEmpty(maxBytes);
struct mScriptString* data = value->value.string;
ssize_t bytes = SocketRecv(ssock->socket, data->buffer, maxBytes);
if (bytes <= 0) {
data->size = 0;
_mScriptSocketSetError(ssock, SocketError());
} else {
data->size = bytes;
ssock->error = mSCRIPT_SOCKERR_OK;
}
return value;
}
// This works sufficiently well for a single socket, but it could be better.
// Ideally, all sockets would be tracked and selected on together for efficiency.
uint32_t _mScriptSocketSelectOne(struct mScriptSocket* ssock, int64_t timeoutMillis) {
Socket reads[] = { ssock->socket };
Socket errors[] = { ssock->socket };
int result = SocketPoll(1, reads, NULL, errors, timeoutMillis);
if (!result) {
return 0;
} else if (errors[0] != INVALID_SOCKET) {
_mScriptSocketSetError(ssock, SocketError());
return -1;
}
return 1;
}
mSCRIPT_BIND_FUNCTION(mScriptSocketCreate_Binding, W(mScriptSocket), _mScriptSocketCreate, 0);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptSocket, close, _mScriptSocketClose, 0);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, W(mScriptSocket), accept, _mScriptSocketAccept, 0);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, open, _mScriptSocketOpen, 2, CHARP, address, U16, port);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, connect, _mScriptSocketConnect, 2, CHARP, address, U16, port);
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptSocket, S32, listen, _mScriptSocketListen, 1, U32, queueLength);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, send, _mScriptSocketSend, 1, STR, data);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, WSTR, recv, _mScriptSocketRecv, 1, U32, maxBytes);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, select, _mScriptSocketSelectOne, 1, S64, timeoutMillis);
mSCRIPT_DEFINE_STRUCT(mScriptSocket)
mSCRIPT_DEFINE_CLASS_DOCSTRING("An internal implementation of a TCP network socket.")
mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptSocket, close)
mSCRIPT_DEFINE_DOCSTRING("Closes the socket. If the socket is already closed, this function does nothing.")
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, close)
mSCRIPT_DEFINE_DOCSTRING("Creates a new socket for an incoming connection from a listening server socket.")
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, accept)
mSCRIPT_DEFINE_DOCSTRING(
"Binds the socket to a specified address and port. "
"If no address is specified, the socket is bound to all network interfaces."
)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, open)
mSCRIPT_DEFINE_DOCSTRING(
"Opens a TCP connection to the specified address and port.\n"
"**Caution:** This is a blocking call. The emulator will not respond until "
"the connection either succeeds or fails."
)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, connect)
mSCRIPT_DEFINE_DOCSTRING(
"Begins listening for incoming connections. The socket must have first been "
"bound with the `open` function."
)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, listen)
mSCRIPT_DEFINE_DOCSTRING(
"Sends data over a socket. Returns the number of bytes written, or -1 if an "
"error occurs."
)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, send)
mSCRIPT_DEFINE_DOCSTRING(
"Reads data from a socket, up to the specified number of bytes. "
"If the socket has been disconnected, this function returns an empty string. "
"Use `select` to test if data is available to be read."
)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, recv)
mSCRIPT_DEFINE_DOCSTRING(
"Checks the status of the socket. "
"Returns 1 if data is available to be read. "
"Returns -1 if an error has occurred on the socket."
)
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, select)
mSCRIPT_DEFINE_DOCSTRING(
"One of the `SOCKERR` constants describing the last error on the socket."
)
mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptSocket, S32, error)
mSCRIPT_DEFINE_END;
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptSocket, listen)
mSCRIPT_S32(1)
mSCRIPT_DEFINE_DEFAULTS_END;
void mScriptContextAttachSocket(struct mScriptContext* context) {
mScriptContextExportNamespace(context, "_socket", (struct mScriptKVPair[]) {
mSCRIPT_KV_PAIR(create, &mScriptSocketCreate_Binding),
mSCRIPT_KV_SENTINEL
});
mScriptContextSetDocstring(context, "_socket", "Basic TCP sockets library");
mScriptContextSetDocstring(context, "_socket.create", "Creates a new socket object");
mScriptContextExportConstants(context, "SOCKERR", (struct mScriptKVPair[]) {
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, UNKNOWN_ERROR),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, OK),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, AGAIN),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, ADDRESS_IN_USE),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, CONNECTION_REFUSED),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, DENIED),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, FAILED),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, NETWORK_UNREACHABLE),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, NOT_FOUND),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, NO_DATA),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, OUT_OF_MEMORY),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, TIMEOUT),
mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, UNSUPPORTED),
mSCRIPT_KV_SENTINEL
});
}