From 882a55247c66839ec3ec95f62217b4077b10b1d6 Mon Sep 17 00:00:00 2001 From: robtweed Date: Fri, 22 Sep 2023 17:49:33 +0100 Subject: [PATCH 01/12] Converted to module --- experimental/application.js | 42 +++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/experimental/application.js b/experimental/application.js index 8433ffe..21a8228 100644 --- a/experimental/application.js +++ b/experimental/application.js @@ -9,7 +9,7 @@ // // // -// function web +// function ./application.mjs // servers NodeJS // // @@ -23,20 +23,30 @@ // // // simple test function -function web(cgi, content, sys) { - let res = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"; - for (argc = 0; argc < cgi.length; argc++) { - res = res + "CGI variable " + cgi[argc].name + " : " + cgi[argc].value + "\r\n"; - } - for (argc = 0; argc < sys.length; argc++) { - if (sys[argc].name === 'function' || sys[argc].name === 'path' || sys[argc].name === 'no') { - res = res + "SYS variable " + sys[argc].name + " : " + sys[argc].value + "\r\n"; - } - } - res = res + "Request payload: " + content.toString(); - return res; -} +let handler = function(cgi, content, sys) { + + console.log('*** application.mjs ****'); + console.log('cgi:'); + console.log(cgi); + + console.log('content:'); + console.log(content); -module.exports = { - web: web + console.log('sys:'); + console.log(sys); + + let res = ''; + cgi.forEach((value, name) => { + res = res + "CGI variable " + name + " : " + value + "\r\n"; + }); + + sys.forEach((value, name) => { + if (name === 'function' || name === 'path' || name === 'no') { + res = res + "SYS variable " + name + " : " + value + "\r\n"; + } + }); + res = res + "Request payload: " + content.toString(); + return res; } + +export {handler}; From 9c9ed711a31ef8dd71f9a78f9556e8301e6bf552 Mon Sep 17 00:00:00 2001 From: robtweed Date: Fri, 22 Sep 2023 18:18:41 +0100 Subject: [PATCH 02/12] Update and rename mg_web_node.js to mg_web_node.mjs --- experimental/mg_web_node.js | 263 -------------------------------- experimental/mg_web_node.mjs | 281 +++++++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 263 deletions(-) delete mode 100644 experimental/mg_web_node.js create mode 100644 experimental/mg_web_node.mjs diff --git a/experimental/mg_web_node.js b/experimental/mg_web_node.js deleted file mode 100644 index 6eb7909..0000000 --- a/experimental/mg_web_node.js +++ /dev/null @@ -1,263 +0,0 @@ -// -// ---------------------------------------------------------------------------- -// | Package: mg_web_node | -// | OS: Unix/Windows | -// | Description: A Node.js server for mg_web | -// | Author: Chris Munt cmunt@mgateway.com | -// | chris.e.munt@gmail.com | -// | Copyright(c) 2023 - 2023 MGateway Ltd | -// | Surrey UK. | -// | All rights reserved. | -// | | -// | http://www.mgateway.com | -// | | -// | 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. | -// | | -// ---------------------------------------------------------------------------- -// - -const net = require('net'); -const cluster = require('node:cluster'); -const cpus = require('node:os').cpus().length; -const process = require('node:process'); - -let port = 7041; -if (process.argv.length > 2) { - port = parseInt(process.argv[2]); -} -let app = './application.js'; -if (process.argv.length > 3) { - app = process.argv[3]; -} -const launch = require(app); - -let buffer = ""; - -if (cluster.isMaster) { - console.log('mg_web server for Node.js %s; CPUs=%d; pid=%d;', process.version, cpus, process.pid); - - let server = net.createServer(); - - server.on('connection', handle_connection); - - server.listen(port, function() { - console.log('mg_web server listening on %j;', server.address()); - }); - - function handle_connection(conn) { - let remote_address = conn.remoteAddress + ':' + conn.remotePort; - console.log('mg_web new client connection from %s', remote_address); - conn.on('data', (d) => { - wk = cluster.fork(); - wk.send(d, conn); - }); - } -} -else { - worker(); -} -function worker() { - - let data_properties = { len: 0, type: 0, sort: 0 }; - let buffer = new Uint8Array(2048); - - process.on('message', (dbx, conn) => { - - conn.on('data', onConnData); - conn.once('close', onConnClose); - conn.on('error', onConnError); - - let remote_address = conn.remoteAddress + ':' + conn.remotePort; - console.log('mg_web new worker process created pid=%d; client=%s', process.pid, remote_address); - - // pretend we're an IRIS server - let offset = 0; - let zv = "IRIS for Windows (x86-64) 2022.3 (Build 589U) Fri Jan 6 2023 00:06:23 EST"; - offset = block_add_string(buffer, offset, zv, zv.length, 0, 0); - conn.write(buffer.slice(0, offset)); - - function onConnData(data) { - let offset = 0; - let request_no = 0; - let cgi = []; - let sys = []; - let content = ""; - let tlen = get_size(data, offset); - let cmnd = data[4]; - offset += 5; - let obufsize = get_size(data, offset); - let utf16 = data[offset + 5]; - offset += 5; - let idx = get_size(data, offset); - offset += 5; - //console.log('request tlen=%d; cmnd=%d; obufsize=%d; utf16=%d; idx=%d;', tlen, cmnd, obufsize, utf16, idx); - - let len = 0; - let doffset = 0; - let dlen = 0; - let fun = ""; - let ctx = ""; - let param = ""; - for (let argc = 0; argc < 10; argc++) { - len = block_get_size(data, offset, data_properties) - //console.log(' >>> item argc=%d; offset=%d; len=%d; type=%d; sort=%d;', argc, offset, len, data_properties.type, data_properties.sort); - offset += 5; - if (argc === 0) { - // dbxweb^%zmgsis - fun = data.slice(offset, offset + len).toString(); - } - else if (argc === 1) { - // arg 1: context - ctx = data.slice(offset, offset + len).toString(); - } - else if (argc === 2) { - // arg 2: HTTP request data - doffset = offset; - dlen = len; - } - else if (argc === 3) { - // arg 3: parameters - param = data.slice(offset, offset + len).toString(); - } - offset += len; - if (data_properties.sort === 9) { - break; - } - } - //console.log('fun=%s; ctx=%s; param=%s;', fun, ctx, param); - - // unpack HTTP request data into cgi array, sys array and content (request payload) - offset = doffset; - for (let argc = 0; argc < 1000; argc++) { - len = block_get_size(data, offset, data_properties) - //console.log(' >>> web item offset=%d; len=%d; type=%d; sort=%d; data=%s', offset, len, data_properties.type, data_properties.sort, data.slice(offset + 5, offset + 5 + len).toString()); - offset += 5; - if (data_properties.sort === 5) { - // CGI environment variable - let d = data.slice(offset, offset + len).toString().split("="); - cgi.push(new nvpair(d[0], d[1])); - } - if (data_properties.sort === 6) { - // request payload (if any) - content = data.slice(offset, offset + len); - } - if (data_properties.sort === 8) { - // system variable - let d = data.slice(offset, offset + len).toString().split("="); - if (d[0] === "no") { - d[1] = get_size(data, offset + 3); - request_no = d[1]; - } - else if (d[0] === "function") { - fun = d[1]; - } - sys.push(new nvpair(d[0], d[1])); - } - offset += len; - if (data_properties.sort === 9) { - break; - } - } - - // notify mg_web of data framing protocol in use for response - offset = 0; - offset = add_head(buffer, offset, 0, 0); - offset = add_head(buffer, offset, request_no, 1); - conn.write(buffer.slice(0, offset), 'binary'); - - // ******* call-out to application - START ******* - // CGI variables in 'cgi' array; system variables in 'sys' array; request payload in 'content' - // generate a resonse in variable 'res' - eval('res = launch.' + fun + '(cgi, content, sys)'); - // ******* call-out to application - END ******* - - offset = 0; - offset = block_add_chunk(buffer, offset, res, res.length); - offset = set_term(buffer, offset); - conn.write(buffer.slice(0,offset), 'binary'); - } - - function onConnClose() { - console.log('connection closed'); - } - - function onConnError(err) { - console.log('Connection error: %s', err.message); - } - - }); - -} - -class nvpair { - name; - value; - constructor(name, value) { - this.name = name; - this.value = value; - } -} -function block_copy(buffer_to, offset, buffer_from, from, to) { - for (let i = from; i < to; i++) { - buffer_to[offset++] = buffer_from[i]; - } - return offset; -} -function block_add_string(buffer, offset, data, data_len, data_sort, data_type) { - offset = block_add_size(buffer, offset, data_len, data_sort, data_type); - for (let i = 0; i < data_len; i++) { - buffer[offset++] = data.charCodeAt(i); - } - return offset; -} -function block_add_chunk(buffer, offset, data, data_len) { - offset = set_size(buffer, offset, data_len); - for (let i = 0; i < data_len; i++) { - buffer[offset++] = data.charCodeAt(i); - } - return offset; -} -function block_add_size(buffer, offset, data_len, data_sort, data_type) { - offset = set_size(buffer, offset, data_len); - buffer[offset] = ((data_sort * 20) + data_type); - return (offset + 1); -} -function add_head(buffer, offset, data_len, cmnd) { - offset = set_size(buffer, offset, data_len); - buffer[offset] = cmnd; - return (offset + 1); -} -function block_get_size(buffer, offset, data_properties) { - data_properties.len = get_size(buffer, offset); - data_properties.sort = buffer[offset + 4]; - data_properties.type = data_properties.sort % 20; - data_properties.sort = Math.floor(data_properties.sort / 20); - return data_properties.len; -} -function set_term(buffer, offset) { - buffer[offset + 0] = 255; - buffer[offset + 1] = 255; - buffer[offset + 2] = 255; - buffer[offset + 3] = 255; - return (offset + 4) -} -function set_size(buffer, offset, data_len) { - buffer[offset + 0] = (data_len >> 0); - buffer[offset + 1] = (data_len >> 8); - buffer[offset + 2] = (data_len >> 16); - buffer[offset + 3] = (data_len >> 24); - return (offset + 4) -} -function get_size(buffer, offset) { - return ((buffer[offset + 0]) | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); -} diff --git a/experimental/mg_web_node.mjs b/experimental/mg_web_node.mjs new file mode 100644 index 0000000..c7201bb --- /dev/null +++ b/experimental/mg_web_node.mjs @@ -0,0 +1,281 @@ +// +// ---------------------------------------------------------------------------- +// | Package: mg_web_node | +// | OS: Unix/Windows | +// | Description: A Node.js server for mg_web | +// | Author: Chris Munt cmunt@mgateway.com | +// | chris.e.munt@gmail.com | +// | Copyright(c) 2023 - 2023 MGateway Ltd | +// | Surrey UK. | +// | All rights reserved. | +// | | +// | http://www.mgateway.com | +// | | +// | 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. | +// | | +// ---------------------------------------------------------------------------- +// + +import net from 'node:net'; +import cluster from 'node:cluster'; +import os from 'node:os'; +import process from 'node:process'; + +const cpus = os.cpus().length; + +let port = 7041; +if (process.argv.length > 2) { + port = parseInt(process.argv[2]); +} + +if (process.argv.length > 3) { + app = process.argv[3]; +} + +if (cluster.isPrimary) { + console.log('mg_web server for Node.js %s; CPUs=%d; pid=%d;', process.version, cpus, process.pid); + + let server = net.createServer(); + + server.on('connection', (conn) => { + let remote_address = conn.remoteAddress + ':' + conn.remotePort; + console.log('mg_web new client connection from %s', remote_address); + + conn.on('data', (d) => { + let worker = cluster.fork(); + + worker.on('message', message => { + if (message === 'ready!') { + worker.send(d, conn); + } + }); + }); + }); + + server.listen(port, () => { + console.log('mg_web server listening on %j;', server.address()); + }); +} +else { + + let data_properties = { len: 0, type: 0, sort: 0 }; + let buffer = new Uint8Array(2048); + let handlers = new Map(); + + function block_add_string(buffer, offset, data, data_len, data_sort, data_type) { + offset = block_add_size(buffer, offset, data_len, data_sort, data_type); + for (let i = 0; i < data_len; i++) { + buffer[offset++] = data.charCodeAt(i); + } + return offset; + } + + function block_add_size(buffer, offset, data_len, data_sort, data_type) { + offset = set_size(buffer, offset, data_len); + buffer[offset] = ((data_sort * 20) + data_type); + return (offset + 1); + } + + function block_add_chunk(buffer, offset, data, data_len) { + offset = set_size(buffer, offset, data_len); + for (let i = 0; i < data_len; i++) { + buffer[offset++] = data.charCodeAt(i); + } + return offset; + } + + function add_head(buffer, offset, data_len, cmnd) { + offset = set_size(buffer, offset, data_len); + buffer[offset] = cmnd; + return (offset + 1); + } + + function block_get_size(buffer, offset, data_properties) { + data_properties.len = get_size(buffer, offset); + data_properties.sort = buffer[offset + 4]; + data_properties.type = data_properties.sort % 20; + data_properties.sort = Math.floor(data_properties.sort / 20); + return data_properties.len; + } + + function set_term(buffer, offset) { + buffer[offset + 0] = 255; + buffer[offset + 1] = 255; + buffer[offset + 2] = 255; + buffer[offset + 3] = 255; + return (offset + 4); + } + + function set_size(buffer, offset, data_len) { + buffer[offset + 0] = (data_len >> 0); + buffer[offset + 1] = (data_len >> 8); + buffer[offset + 2] = (data_len >> 16); + buffer[offset + 3] = (data_len >> 24); + return (offset + 4); + } + + function get_size(buffer, offset) { + return ((buffer[offset + 0]) | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); + } + + process.on('message', (dbx, conn) => { + + let remote_address = conn.remoteAddress + ':' + conn.remotePort; + console.log('mg_web new worker process created pid=%d; client=%s', process.pid, remote_address); + + // pretend we're an IRIS server + let offset = 0; + let zv = "IRIS for Windows (x86-64) 2022.3 (Build 589U) Fri Jan 6 2023 00:06:23 EST"; + offset = block_add_string(buffer, offset, zv, zv.length, 0, 0); + conn.write(buffer.slice(0, offset)); + + conn.on('data', async (data) => { + let offset = 0; + let request_no = 0; + let cgi = new Map(); + let sys = new Map(); + let content = ""; + let tlen = get_size(data, offset); + let cmnd = data[4]; + offset += 5; + let obufsize = get_size(data, offset); + let utf16 = data[offset + 5]; + offset += 5; + let idx = get_size(data, offset); + offset += 5; + //console.log('request tlen=%d; cmnd=%d; obufsize=%d; utf16=%d; idx=%d;', tlen, cmnd, obufsize, utf16, idx); + + let len = 0; + let doffset = 0; + let dlen = 0; + let fun = ""; + let ctx = ""; + let param = ""; + for (let argc = 0; argc < 10; argc++) { + len = block_get_size(data, offset, data_properties) + //console.log(' >>> item argc=%d; offset=%d; len=%d; type=%d; sort=%d;', argc, offset, len, data_properties.type, data_properties.sort); + offset += 5; + if (argc === 0) { + // dbxweb^%zmgsis + fun = data.slice(offset, offset + len).toString(); + } + else if (argc === 1) { + // arg 1: context + ctx = data.slice(offset, offset + len).toString(); + } + else if (argc === 2) { + // arg 2: HTTP request data + doffset = offset; + dlen = len; + } + else if (argc === 3) { + // arg 3: parameters + param = data.slice(offset, offset + len).toString(); + } + offset += len; + if (data_properties.sort === 9) { + break; + } + } + //console.log('fun=%s; ctx=%s; param=%s;', fun, ctx, param); + + // unpack HTTP request data into cgi array, sys array and content (request payload) + offset = doffset; + for (let argc = 0; argc < 1000; argc++) { + len = block_get_size(data, offset, data_properties) + //console.log(' >>> web item offset=%d; len=%d; type=%d; sort=%d; data=%s', offset, len, data_properties.type, data_properties.sort, data.slice(offset + 5, offset + 5 + len).toString()); + offset += 5; + if (data_properties.sort === 5) { + // CGI environment variable + let d = data.slice(offset, offset + len).toString().split("="); + cgi.set(d[0], d[1]); + } + if (data_properties.sort === 6) { + // request payload (if any) + content = data.slice(offset, offset + len); + } + if (data_properties.sort === 8) { + // system variable + let d = data.slice(offset, offset + len).toString().split("="); + if (d[0] === "no") { + d[1] = get_size(data, offset + 3); + request_no = d[1]; + } + else if (d[0] === "function") { + fun = d[1]; + } + sys.set(d[0], d[1]); + } + offset += len; + if (data_properties.sort === 9) { + break; + } + } + + // notify mg_web of data framing protocol in use for response + offset = 0; + offset = add_head(buffer, offset, 0, 0); + offset = add_head(buffer, offset, request_no, 1); + conn.write(buffer.slice(0, offset), 'binary'); + + // ******* call-out to application - START ******* + // CGI variables in 'cgi' array; system variables in 'sys' array; request payload in 'content' + // generate a resonse in variable 'res' + + if (!handlers.has(fun)) { + try { + let {handler} = await import(fun); + handlers.set(fun, handler); + } + catch(err) { + console.log('Unable to load handler module'); + console.log(err); + res = "HTTP/1.1 400 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"; + offset = 0; + offset = block_add_chunk(buffer, offset, res, res.length); + offset = set_term(buffer, offset); + conn.write(buffer.slice(0,offset), 'binary'); + } + } + + let res = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"; + try { + res += handlers.get(fun)(cgi, content, sys); + } + catch(err) { + console.log('Handler error!'); + console.log(err); + res = "HTTP/1.1 400 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"; + } + + // ******* call-out to application - END ******* + + offset = 0; + offset = block_add_chunk(buffer, offset, res, res.length); + offset = set_term(buffer, offset); + conn.write(buffer.slice(0,offset), 'binary'); + }); + + + conn.once('close', () => { + console.log('connection closed'); + }); + + conn.on('error', () => { + console.log('Connection error: %s', err.message); + }); + }); + + process.send('ready!'); + +} From dc298bedbbf04f8a97dcb24484c48df3b1e9d6d7 Mon Sep 17 00:00:00 2001 From: robtweed Date: Fri, 22 Sep 2023 18:19:19 +0100 Subject: [PATCH 03/12] Rename to application.mjs From 386e3f6f2b54adc66ef2fd0773f8e44441ca7a69 Mon Sep 17 00:00:00 2001 From: robtweed Date: Fri, 22 Sep 2023 18:19:38 +0100 Subject: [PATCH 04/12] Update and rename application.js to application.mjs --- experimental/{application.js => application.mjs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename experimental/{application.js => application.mjs} (100%) diff --git a/experimental/application.js b/experimental/application.mjs similarity index 100% rename from experimental/application.js rename to experimental/application.mjs From ad85a70ff5408c91810605d16d581394662d98dd Mon Sep 17 00:00:00 2001 From: robtweed Date: Thu, 12 Oct 2023 16:16:28 +0100 Subject: [PATCH 05/12] primary boolean value; added async handler call --- experimental/mg_web_node.mjs | 37 +++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/experimental/mg_web_node.mjs b/experimental/mg_web_node.mjs index c7201bb..c04e4be 100644 --- a/experimental/mg_web_node.mjs +++ b/experimental/mg_web_node.mjs @@ -27,22 +27,29 @@ // import net from 'node:net'; -import cluster from 'node:cluster'; import os from 'node:os'; import process from 'node:process'; +import child_process from 'node:child_process' const cpus = os.cpus().length; let port = 7041; +let app = "application.mjs"; +let primary = true; +let node_path = process.argv[0]; +let mod_name = process.argv[1]; + if (process.argv.length > 2) { port = parseInt(process.argv[2]); } - +if (port === 1000000) { + primary = false; +} if (process.argv.length > 3) { app = process.argv[3]; } -if (cluster.isPrimary) { +if (primary) { console.log('mg_web server for Node.js %s; CPUs=%d; pid=%d;', process.version, cpus, process.pid); let server = net.createServer(); @@ -52,8 +59,7 @@ if (cluster.isPrimary) { console.log('mg_web new client connection from %s', remote_address); conn.on('data', (d) => { - let worker = cluster.fork(); - + let worker = child_process.fork(mod_name, [1000000], { stdio: ['inherit', 'inherit', 'inherit', 'ipc'] }); worker.on('message', message => { if (message === 'ready!') { worker.send(d, conn); @@ -98,7 +104,7 @@ else { offset = set_size(buffer, offset, data_len); buffer[offset] = cmnd; return (offset + 1); - } + } function block_get_size(buffer, offset, data_properties) { data_properties.len = get_size(buffer, offset); @@ -133,9 +139,12 @@ else { let remote_address = conn.remoteAddress + ':' + conn.remotePort; console.log('mg_web new worker process created pid=%d; client=%s', process.pid, remote_address); - // pretend we're an IRIS server + // turn the Nagle algorithm off + conn.setNoDelay(); + + // tell the web server what we are let offset = 0; - let zv = "IRIS for Windows (x86-64) 2022.3 (Build 589U) Fri Jan 6 2023 00:06:23 EST"; + let zv = "Node.js " + process.version; offset = block_add_string(buffer, offset, zv, zv.length, 0, 0); conn.write(buffer.slice(0, offset)); @@ -248,9 +257,15 @@ else { } } - let res = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"; + let res = ""; try { - res += handlers.get(fun)(cgi, content, sys); + let fn = handlers.get(fun); + if (fn.constructor.name === 'AsyncFunction') { + res = await fn(cgi, content, sys); + } + else { + res = fn(cgi, content, sys); + } } catch(err) { console.log('Handler error!'); @@ -271,7 +286,7 @@ else { console.log('connection closed'); }); - conn.on('error', () => { + conn.on('error', (err) => { console.log('Connection error: %s', err.message); }); }); From b3d143ffa31023e9111e8ceb825265cc5c3835e1 Mon Sep 17 00:00:00 2001 From: robtweed Date: Fri, 13 Oct 2023 16:21:39 +0100 Subject: [PATCH 06/12] Passed socket descriptor via sys to handler and removed close event handler --- experimental/mg_web_node.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/experimental/mg_web_node.mjs b/experimental/mg_web_node.mjs index c04e4be..9ecce56 100644 --- a/experimental/mg_web_node.mjs +++ b/experimental/mg_web_node.mjs @@ -260,6 +260,7 @@ else { let res = ""; try { let fn = handlers.get(fun); + sys.set('socket', conn); if (fn.constructor.name === 'AsyncFunction') { res = await fn(cgi, content, sys); } @@ -282,9 +283,9 @@ else { }); - conn.once('close', () => { - console.log('connection closed'); - }); + //conn.once('close', () => { + // console.log('connection closed'); + //}); conn.on('error', (err) => { console.log('Connection error: %s', err.message); From 49b2581c5ebc86ef431430a6ae0b3a515895493c Mon Sep 17 00:00:00 2001 From: robtweed Date: Sun, 15 Oct 2023 15:54:29 +0100 Subject: [PATCH 07/12] Suggested changes for mg-dbx-napi packaging --- mgdbx/package.json | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 mgdbx/package.json diff --git a/mgdbx/package.json b/mgdbx/package.json new file mode 100644 index 0000000..c5e5239 --- /dev/null +++ b/mgdbx/package.json @@ -0,0 +1,52 @@ +{ + "author": "Chris Munt (http://www.gateway.com/)", + "name": "mg-dbx-napi", + "description": "High speed Synchronous and Asynchronous access to InterSystems Cache/IRIS and YottaDB from Node.js or Bun.", + "version": "1.1.3c", + "maintainers": "Chris Munt ", + "homepage": "https://github.com/chrisemunt/mg-dbx-napi", + "repository": { + "type": "git", + "url": "git+https://github.com/chrisemunt/mg-dbx-napi.git" + }, + "bugs": { + "url": "https://github.com/chrisemunt/mg-dbx-napi/issues" + }, + "os": [ + "linux", + "darwin", + "win32" + ], + "keywords": [ + "nosql", + "intersystems", + "cache", + "iris", + "yottadb", + "m", + "mumps" + ], + "scripts": { + "install": "node-gyp rebuild", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "main": "./build/Release/mg-dbx-napi", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.x" + }, + "exports": { + ".": "./src/shimloader.mjs", + "./node": "./src/mg_dbx_napi.mjs", + "./bun": "./src/mg_dbx_napi.ts", + "./arm64": "./build/Release/mg-dbx-napi.node", + "./arm": "./build/Release/mg-dbx-napi.node", + "./x64": "./build/Release/mg-dbx-napi.node", + "./darwin": "./build/Release/mg-dbx-napi.node", + "./win": "./bin/winx64/mg-dbx-napi.node" + }, + "gypfile": true, + "directories": { + "doc": "doc" + } +} From c5bcdecc11e6440c0f8d9361a6e412aa2357620b Mon Sep 17 00:00:00 2001 From: robtweed Date: Sun, 15 Oct 2023 15:55:37 +0100 Subject: [PATCH 08/12] Create shimloader.mjs --- mgdbx/shimloader.mjs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 mgdbx/shimloader.mjs diff --git a/mgdbx/shimloader.mjs b/mgdbx/shimloader.mjs new file mode 100644 index 0000000..634dcf3 --- /dev/null +++ b/mgdbx/shimloader.mjs @@ -0,0 +1,33 @@ +import { createRequire } from "module"; +const require = createRequire(import.meta.url); + +// Load the correct JavaScript shim +let mod; +if (typeof Bun === 'undefined') { + mod = await import('mg-dbx-napi/node'); +} +else { + mod = await import('mg-dbx-napi/bun'); +} + +let mgdbx = mod.mgdbx; +let obj = {}; + +let arch = process.arch; +if (arch === 'x64' && process.platform === 'win32') arch = 'win'; +if (arch === 'x64' && process.platform === 'darwin') arch = 'darwin'; + +if (['win', 'arm', 'arm64', 'x64', 'darwin'].includes(arch)) { + let dbx = require('mg-dbx-napi/' + arch); + obj = mgdbx(dbx); +} + +let server = obj.server; +let mglobal = obj.mglobal; +let mclass = obj.mclass; + +export { + server, + mglobal, + mclass +}; From 8d0573a6acd5201af090f5e66dea819bec42f82a Mon Sep 17 00:00:00 2001 From: robtweed Date: Sun, 15 Oct 2023 15:56:38 +0100 Subject: [PATCH 09/12] Create mg_dbx_napi.mjs --- mgdbx/mg_dbx_napi.mjs | 1338 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1338 insertions(+) create mode 100644 mgdbx/mg_dbx_napi.mjs diff --git a/mgdbx/mg_dbx_napi.mjs b/mgdbx/mg_dbx_napi.mjs new file mode 100644 index 0000000..e9a7888 --- /dev/null +++ b/mgdbx/mg_dbx_napi.mjs @@ -0,0 +1,1338 @@ +// +// ---------------------------------------------------------------------------- +// | Package: mg_dbx_napi | +// | OS: Unix/Windows | +// | Description: An Interface to InterSystems Cache/IRIS and YottaDB | +// | Author: Chris Munt cmunt@mgateway.com | +// | chris.e.munt@gmail.com | +// | Copyright(c) 2019 - 2023 MGateway Ltd | +// | Surrey UK. | +// | All rights reserved. | +// | | +// | http://www.mgateway.com | +// | | +// | 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. | +// | | +// ---------------------------------------------------------------------------- +// + +const DBX_VERSION_MAJOR = 1; +const DBX_VERSION_MINOR = 1; +const DBX_VERSION_BUILD = 3; + +const DBX_DSORT_INVALID = 0; +const DBX_DSORT_DATA = 1; +const DBX_DSORT_SUBSCRIPT = 2; +const DBX_DSORT_GLOBAL = 3; +const DBX_DSORT_EOD = 9; +const DBX_DSORT_STATUS = 10; +const DBX_DSORT_ERROR = 11; + +const DBX_DTYPE_NONE = 0; +const DBX_DTYPE_STR = 1; +const DBX_DTYPE_STR8 = 2; +const DBX_DTYPE_STR16 = 3; +const DBX_DTYPE_INT = 4; +const DBX_DTYPE_INT64 = 5; +const DBX_DTYPE_DOUBLE = 6; +const DBX_DTYPE_OREF = 7; +const DBX_DTYPE_NULL = 10; + +const DBX_CMND_OPEN = 1; +const DBX_CMND_CLOSE = 2; +const DBX_CMND_NSGET = 3; +const DBX_CMND_NSSET = 4; + +const DBX_CMND_GSET = 11; +const DBX_CMND_GGET = 12; +const DBX_CMND_GNEXT = 13; +const DBX_CMND_GPREVIOUS = 14; +const DBX_CMND_GDELETE = 15; +const DBX_CMND_GDEFINED = 16; +const DBX_CMND_GINCREMENT = 17; +const DBX_CMND_GLOCK = 18; +const DBX_CMND_GUNLOCK = 19 +const DBX_CMND_GMERGE = 20 + +const DBX_CMND_FUNCTION = 31; + +const DBX_CMND_CCMETH = 41; +const DBX_CMND_CGETP = 42; +const DBX_CMND_CSETP = 43; +const DBX_CMND_CMETH = 44; +const DBX_CMND_CCLOSE = 45; + +const DBX_CMND_TSTART = 61; +const DBX_CMND_TLEVEL = 62; +const DBX_CMND_TCOMMIT = 63; +const DBX_CMND_TROLLBACK = 64; + +const DBX_INPUT_BUFFER_SIZE = 3641145; // or 32768 + +let mgdbx = function(dbx) { + + class server { + type = ""; + path = ""; + host = ""; + tcp_port = 0; + username = ""; + password = ""; + namespace = ""; + env_vars = ""; + debug = ""; + server = ""; + server_software = ""; + timeout = 60; + init = 0; + index = 0; + buffer = [0, 0, 0, 0, 0, 0, 0, 0]; + buffer_size = [0, 0, 0, 0, 0, 0, 0, 0]; + + constructor(...args) { + this.buffer[0] = new Uint8Array(DBX_INPUT_BUFFER_SIZE); + this.buffer_size[0] = DBX_INPUT_BUFFER_SIZE; + return; + } + + get_buffer() { + let bidx = 0; + return bidx; + } + + release_buffer(bidx) { + return bidx; + } + + version() { + return dbx.version(); + } + + dbversion() { + if (this.init === 0) { + const ret = dbx.init(); + this.init ++; + } + return dbx.dbversion(); + } + + open(...args) { + let offset = 0; + let request = { command: DBX_CMND_OPEN, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + const ret = dbx.init(); + this.init ++; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + if (args.length > 0) { + if (typeof args[0] === 'object') { + if (args[0].hasOwnProperty('type')) { + this.type = args[0].type; + } + if (args[0].hasOwnProperty('path')) { + this.path = args[0].path; + } + if (args[0].hasOwnProperty('host')) { + this.host = args[0].host; + } + if (args[0].hasOwnProperty('tcp_port')) { + this.tcp_port = args[0].tcp_port; + } + if (args[0].hasOwnProperty('username')) { + this.username = args[0].username; + } + if (args[0].hasOwnProperty('password')) { + this.password = args[0].password; + } + if (args[0].hasOwnProperty('env_vars')) { + if (typeof args[0].env_vars === 'object') { + let envvars = ''; + for (const name in args[0].env_vars) { + envvars = envvars + name + '=' + args[0].env_vars[name] + '\n'; + } + envvars = envvars + '\n'; + this.env_vars = envvars; + } + else { + this.env_vars = args[0].env_vars; + } + } + if (args[0].hasOwnProperty('debug')) { + this.debug = args[0].debug; + } + if (args[0].hasOwnProperty('timeout')) { + this.timeout = args[0].timeout; + } + } + } + + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, this.type, this.type.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.path, this.path.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.host, this.host.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.tcp_port.toString(), this.tcp_port.toString().length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_string(this.buffer[bidx], offset, this.username, this.username.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.password, this.password.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.namespace, this.namespace.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.debug, this.debug.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.env_vars, this.env_vars.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.server, this.server.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.server_software, this.server_software.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.timeout.toString(), this.timeout.toString().length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + close(...args) { + let offset = 0; + let request = { command: DBX_CMND_CLOSE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + current_namespace(...args) { + let offset = 0; + let request = { command: DBX_CMND_NSGET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + if (args.length > 0) { + request.command = DBX_CMND_NSSET; + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + } + + request.command = DBX_CMND_NSGET; + offset = 0; + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + set(...args) { + let offset = 0; + let request = { command: DBX_CMND_GSET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + get(...args) { + let offset = 0; + let request = { command: DBX_CMND_GGET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + delete(...args) { + let offset = 0; + let request = { command: DBX_CMND_GDELETE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + defined(...args) { + let offset = 0; + let request = { command: DBX_CMND_GDEFINED, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + next(...args) { + let offset = 0; + let request = { command: DBX_CMND_GNEXT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + previous(...args) { + let offset = 0; + let request = { command: DBX_CMND_GPREVIOUS, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + increment(...args) { + let offset = 0; + let request = { command: DBX_CMND_GINCREMENT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + lock(...args) { + let offset = 0; + let request = { command: DBX_CMND_GLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + unlock(...args) { + let offset = 0; + let request = { command: DBX_CMND_GUNLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + tstart(...args) { + let offset = 0; + let request = { command: DBX_CMND_TSTART, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + tlevel(...args) { + let offset = 0; + let request = { command: DBX_CMND_TLEVEL, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + tcommit(...args) { + let offset = 0; + let request = { command: DBX_CMND_TCOMMIT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + trollback(...args) { + let offset = 0; + let request = { command: DBX_CMND_TROLLBACK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + function(...args) { + let offset = 0; + let request = { command: DBX_CMND_FUNCTION, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + sleep(msecs) { + let result = 0; + + result = dbx.sleep(msecs); + return result; + } + + classmethod(...args) { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + const cls = new mclass(this); + cls.class_name = args[0]; + cls.oref = request.result_data; + return cls; + } + + return request.result_data; + } + + benchmark(...args) { + let i = 0; + let data = ""; + + if (this.init === 0) { + const ret = dbx.init(); + this.init ++; + } + + let argc = args.length; + if (argc < 1) { + return data; + } + let context = 0; + if (argc > 1) { + context = args[1]; + } + + let bidx = this.get_buffer(); + let istring = args[0]; + + for (i = 0; i < istring.length; i ++) { + this.buffer[bidx][i] = istring.charCodeAt(i); + } + this.buffer[bidx][i] = 0; + + if (context === 1) { + const pdata = dbx.benchmark(this.buffer[bidx], i, 0, 0); + data = pdata; + } + else { + data = "output string"; + } + this.release_buffer(bidx); + + return data; + } + + benchmarkex(...args) { + let offset = 0; + let request = { command: 0, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + let data = 0; + + if (this.init === 0) { + return data; + } + + let argc = args.length; + if (argc < 1) { + return data; + } + let command = 0; + if (argc === 1) { + request.command = DBX_CMND_GGET; + } + else if (argc === 3) { + request.command = DBX_CMND_GSET; + } + if (request.command === 0) { + return data; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + const pdata = dbx.benchmarkex(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + } + + class mglobal { + db; + global_name = ""; + base_buffer; + base_offset = 0; + + constructor(db, ...args) { + let request = { command: 0, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + this.db = db; + this.base_buffer = new Uint8Array(DBX_INPUT_BUFFER_SIZE); + this.base_offset = 0; + this.base_offset = pack_arguments(this.base_buffer, this.base_offset, this.db.index, args, request, 0); + this.base_offset -= 5; + if (args.length > 0) { + this.global_name = args[0]; + } + return; + } + + set(...args) { + let offset = 0; + let request = { command: DBX_CMND_GSET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + get(...args) { + let offset = 0; + let request = { command: DBX_CMND_GGET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + delete(...args) { + let offset = 0; + let request = { command: DBX_CMND_GDELETE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + defined(...args) { + let offset = 0; + let request = { command: DBX_CMND_GDEFINED, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + next(...args) { + let offset = 0; + let request = { command: DBX_CMND_GNEXT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + previous(...args) { + let offset = 0; + let request = { command: DBX_CMND_GPREVIOUS, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + increment(...args) { + let offset = 0; + let request = { command: DBX_CMND_GINCREMENT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + lock(...args) { + let offset = 0; + let request = { command: DBX_CMND_GLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + unlock(...args) { + let offset = 0; + let request = { command: DBX_CMND_GUNLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + merge(...args) { + let offset = 0; + let sort = 0; + let str = ""; + let request = { command: DBX_CMND_GMERGE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + + for (let argn = 0; argn < args.length; argn ++) { + if (typeof args[argn] === "object" && args[argn].constructor.name == "mglobal") { + offset = block_copy(this.db.buffer[bidx], offset, args[argn].base_buffer, 15, args[argn].base_offset); + } + else if (typeof args[argn] === "string") { + str = args[argn]; + offset = block_add_string(this.db.buffer[bidx], offset, str, str.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + } + else { + str = args[argn].toString(); + offset = block_add_string(this.db.buffer[bidx], offset, str, str.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + } + } + + offset = block_add_string(this.db.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR) + add_head(this.db.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + reset(...args) { + let request = { command: 0, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + this.base_offset = 0; + this.base_offset = pack_arguments(this.base_buffer, this.base_offset, this.db.index, args, request, 0); + this.base_offset -= 5; + if (args.length > 0) { + this.global_name = args[0]; + } + return; + } + + } + + class mclass { + db; + class_name = ""; + oref = ""; + base_buffer; + base_offset = 0; + + constructor(db, ...args) { + this.db = db; + this.base_buffer = new Uint8Array(DBX_INPUT_BUFFER_SIZE); + this.base_offset = 0; + + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, db.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + if (args.length > 0) { + this.class_name = args[0]; + this.base_offset = block_add_string(this.base_buffer, this.base_offset, this.class_name, this.class_name.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + if (args.length > 1 && this.db.init > 0) { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + let bidx = this.db.get_buffer(); + + offset = pack_arguments(this.db.buffer[bidx], offset, db.index, args, request, 0); + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + this.oref = request.result_data; + } + } + } + return; + } + + classmethod(...args) { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + this.oref = request.result_data; + } + return request.result_data; + } + + method(...args) { + let offset = 0; + let request = { command: DBX_CMND_CMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + getproperty(...args) { + let offset = 0; + let request = { command: DBX_CMND_CGETP, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + setproperty(...args) { + let offset = 0; + let request = { command: DBX_CMND_CSETP, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + closeinstance(...args) { + let offset = 0; + let request = { command: DBX_CMND_CCLOSE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + reset(...args) { + this.base_offset = 0; + + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.db.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + if (args.length > 0) { + this.class_name = args[0]; + this.base_offset = block_add_string(this.base_buffer, this.base_offset, this.class_name, this.class_name.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + if (args.length > 1 && this.db.init > 0) { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + let bidx = this.db.get_buffer(); + + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 0); + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + this.oref = request.result_data; + } + } + } + return; + } + } + + async function async_command(db, buffer, buffer_size, request, context, callback) { + let promise = new Promise((resolve, reject) => { + const pdata = dbx.command(buffer, buffer_size, request.command, context); + get_result(buffer, pdata, request); + db.release_buffer(0); + resolve(request.result_data); + }); + let result_data = await promise + if (request.error_message === "") { + callback(false, result_data); + } + else { + callback(true, result_data); + } + + return; + } + + function pack_arguments(buffer, offset, index, args, request, context) { + let str = ""; + + if (context === 0) { + offset = block_add_size(buffer, offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(buffer, offset, buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(buffer, offset, index, DBX_DSORT_DATA, DBX_DTYPE_INT); + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + for (let argn = 0; argn < request.argc; argn ++) { + //console.log(argn, " = ", args[argn], " : ", typeof args[argn]); + if (typeof args[argn] === "string") + str = args[argn]; + else + str = args[argn].toString(); + if (argn == 0) + offset = block_add_string(buffer, offset, str, str.length, DBX_DSORT_GLOBAL, DBX_DTYPE_STR); + else + offset = block_add_string(buffer, offset, str, str.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + } + + offset = block_add_string(buffer, offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR) + add_head(buffer, 0, offset, request.command); + + return offset; + } + + function get_result(pbuffer, pdata, request) { + let data_properties = { len: 0, type: 0, sort: 0 }; + + block_get_size(pbuffer, 0, data_properties); + //console.log("mg_dbx_napi.js data_view data properties => ", data_properties); + + if (data_properties.sort === DBX_DSORT_ERROR) { + if (data_properties.len === 0) { + request.error_message = "" + } + else { + request.error_message = pdata; + } + } + else { + if (data_properties.len === 0) { + request.result_data = "" + } + else { + request.result_data = pdata; + } + } + request.type = data_properties.type; + return request.result_data; + } + + function block_copy(buffer_to, offset, buffer_from, from, to) { + for (let i = from; i < to; i ++) { + buffer_to[offset ++] = buffer_from[i]; + } + return offset; + } + + function block_add_string(buffer, offset, data, data_len, data_sort, data_type) { + offset = block_add_size(buffer, offset, data_len, data_sort, data_type); + for (let i = 0; i < data_len; i ++) { + buffer[offset ++] = data.charCodeAt(i); + } + return offset; + } + + function block_add_size(buffer, offset, data_len, data_sort, data_type) { + buffer[offset + 0] = (data_len >> 0); + buffer[offset + 1] = (data_len >> 8); + buffer[offset + 2] = (data_len >> 16); + buffer[offset + 3] = (data_len >> 24); + buffer[offset + 4] = ((data_sort * 20) + data_type); + return (offset + 5); + } + + function add_head(buffer, offset, data_len, cmnd) { + buffer[offset + 0] = (data_len >> 0); + buffer[offset + 1] = (data_len >> 8); + buffer[offset + 2] = (data_len >> 16); + buffer[offset + 3] = (data_len >> 24); + buffer[offset + 4] = cmnd; + return (offset + 5); + } + + function block_get_size(buffer, offset, data_properties) { + data_properties.len = ((buffer[offset + 0]) | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); + data_properties.sort = buffer[4]; + data_properties.type = data_properties.sort % 20; + data_properties.sort = Math.floor(data_properties.sort / 20); + return data_properties; + } + + return { + server, + mglobal, + mclass + }; +} +export {mgdbx}; From 3f5b3ed631e9b00dca32bc81969b4948156d9574 Mon Sep 17 00:00:00 2001 From: robtweed Date: Sun, 15 Oct 2023 15:57:28 +0100 Subject: [PATCH 10/12] Create mg_dbx_napi.ts --- mgdbx/mg_dbx_napi.ts | 1340 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1340 insertions(+) create mode 100644 mgdbx/mg_dbx_napi.ts diff --git a/mgdbx/mg_dbx_napi.ts b/mgdbx/mg_dbx_napi.ts new file mode 100644 index 0000000..44e285a --- /dev/null +++ b/mgdbx/mg_dbx_napi.ts @@ -0,0 +1,1340 @@ +// +// ---------------------------------------------------------------------------- +// | Package: mg_dbx_napi | +// | OS: Unix/Windows | +// | Description: An Interface to InterSystems Cache/IRIS and YottaDB | +// | Author: Chris Munt cmunt@mgateway.com | +// | chris.e.munt@gmail.com | +// | Copyright(c) 2019 - 2023 MGateway Ltd | +// | Surrey UK. | +// | All rights reserved. | +// | | +// | http://www.mgateway.com | +// | | +// | 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. | +// | | +// ---------------------------------------------------------------------------- +// + + +const DBX_VERSION_MAJOR: number = 1; +const DBX_VERSION_MINOR: number = 1; +const DBX_VERSION_BUILD: number = 3; + +const DBX_DSORT_INVALID: number = 0; +const DBX_DSORT_DATA: number = 1; +const DBX_DSORT_SUBSCRIPT: number = 2; +const DBX_DSORT_GLOBAL: number = 3; +const DBX_DSORT_EOD: number = 9; +const DBX_DSORT_STATUS: number = 10; +const DBX_DSORT_ERROR: number = 11; + +const DBX_DTYPE_NONE: number = 0; +const DBX_DTYPE_STR: number = 1; +const DBX_DTYPE_STR8: number = 2; +const DBX_DTYPE_STR16: number = 3; +const DBX_DTYPE_INT: number = 4; +const DBX_DTYPE_INT64: number = 5; +const DBX_DTYPE_DOUBLE: number = 6; +const DBX_DTYPE_OREF: number = 7; +const DBX_DTYPE_NULL: number = 10; + +const DBX_CMND_OPEN: number = 1; +const DBX_CMND_CLOSE: number = 2; +const DBX_CMND_NSGET: number = 3; +const DBX_CMND_NSSET: number = 4; + +const DBX_CMND_GSET: number = 11; +const DBX_CMND_GGET: number = 12; +const DBX_CMND_GNEXT: number = 13; +const DBX_CMND_GPREVIOUS: number = 14; +const DBX_CMND_GDELETE: number = 15; +const DBX_CMND_GDEFINED: number = 16; +const DBX_CMND_GINCREMENT: number = 17; +const DBX_CMND_GLOCK: number = 18; +const DBX_CMND_GUNLOCK: number = 19 +const DBX_CMND_GMERGE: number = 20 + +const DBX_CMND_FUNCTION: number = 31; + +const DBX_CMND_CCMETH: number = 41; +const DBX_CMND_CGETP: number = 42; +const DBX_CMND_CSETP: number = 43; +const DBX_CMND_CMETH: number = 44; +const DBX_CMND_CCLOSE: number = 45; + +const DBX_CMND_TSTART: number = 61; +const DBX_CMND_TLEVEL: number = 62; +const DBX_CMND_TCOMMIT: number = 63; +const DBX_CMND_TROLLBACK: number = 64; + +const DBX_INPUT_BUFFER_SIZE: number = 3641145; // or 32768; + +type async_callback = (error: boolean, result: string) => void; + +let mgdbx = function(dbx) { + + class server { + type: string = ""; + path: string = ""; + host: string = ""; + tcp_port: number = 0; + username: string = ""; + password: string = ""; + namespace: string = ""; + env_vars: string = ""; + debug: string = ""; + server: string = ""; + server_software: string = ""; + timeout: number = 60; + init: number = 0; + index: number = 0; + buffer = [0, 0, 0, 0, 0, 0, 0, 0]; + buffer_size = [0, 0, 0, 0, 0, 0, 0, 0]; + constructor(...args: any[]) { + this.buffer[0] = new Uint8Array(DBX_INPUT_BUFFER_SIZE); + return; + } + + get_buffer(): number { + let bidx = 0; + return bidx; + } + + release_buffer(bidx: number): number { + return bidx; + } + + version(): string { + return dbx.version(); + } + + dbversion(): string { + if (this.init === 0) { + const ret = dbx.init(); + this.init ++; + } + return dbx.dbversion(); + } + + open(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_OPEN, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + const ret = dbx.init(); + this.init ++; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + if (args.length > 0) { + if (typeof args[0] === 'object') { + if (args[0].hasOwnProperty('type')) { + this.type = args[0].type; + } + if (args[0].hasOwnProperty('path')) { + this.path = args[0].path; + } + if (args[0].hasOwnProperty('host')) { + this.host = args[0].host; + } + if (args[0].hasOwnProperty('tcp_port')) { + this.tcp_port = args[0].tcp_port; + } + if (args[0].hasOwnProperty('username')) { + this.username = args[0].username; + } + if (args[0].hasOwnProperty('password')) { + this.password = args[0].password; + } + if (args[0].hasOwnProperty('env_vars')) { + if (typeof args[0].env_vars === 'object') { + let envvars = ''; + for (const name in args[0].env_vars) { + envvars = envvars + name + '=' + args[0].env_vars[name] + '\n'; + } + envvars = envvars + '\n'; + this.env_vars = envvars; + } + else { + this.env_vars = args[0].env_vars; + } + } + if (args[0].hasOwnProperty('debug')) { + this.debug = args[0].debug; + } + if (args[0].hasOwnProperty('timeout')) { + this.timeout = args[0].timeout; + } + } + } + + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, this.type, this.type.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.path, this.path.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.host, this.host.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.tcp_port.toString(), this.tcp_port.toString().length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_string(this.buffer[bidx], offset, this.username, this.username.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.password, this.password.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.namespace, this.namespace.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.debug, this.debug.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.env_vars, this.env_vars.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.server, this.server.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.server_software, this.server_software.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + offset = block_add_string(this.buffer[bidx], offset, this.timeout.toString(), this.timeout.toString().length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + close(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_CLOSE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + current_namespace(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_NSGET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + if (args.length > 0) { + request.command = DBX_CMND_NSSET; + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request.command, 0); + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + } + + request.command = DBX_CMND_NSGET; + offset = 0; + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + set(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GSET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + get(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GGET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + delete(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GDELETE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + defined(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GDEFINED, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + next(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GNEXT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + previous(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GPREVIOUS, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + increment(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GINCREMENT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + lock(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + unlock(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GUNLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + tstart(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_TSTART, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc--; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + tlevel(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_TLEVEL, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc--; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + tcommit(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_TCOMMIT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc--; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + trollback(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_TROLLBACK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc--; + } + } + + let bidx = this.get_buffer(); + offset = block_add_size(this.buffer[bidx], offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.buffer[bidx].length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(this.buffer[bidx], offset, this.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + offset = block_add_string(this.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR); + add_head(this.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + function(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_FUNCTION, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + + sleep(msecs: number) { + let result = 0; + + result = dbx.sleep(msecs); + return result; + } + + classmethod(...args: any[]): any { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.init === 0) { + return ""; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + if (request.async) { + async_command(this, this.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + const cls = new mclass(this); + cls.class_name = args[0]; + cls.oref = request.result_data; + return cls; + } + + return request.result_data; + } + + benchmark(...args: any[]): string { + let i = 0; + let data = ""; + + if (this.init === 0) { + const ret = dbx.init(); + this.init ++; + } + + let argc = args.length; + if (argc < 1) { + return data; + } + let context = 0; + if (argc > 1) { + context = args[1]; + } + + let bidx = this.get_buffer(); + let istring = args[0]; + + for (i = 0; i < istring.length; i++) { + this.buffer[bidx][i] = istring.charCodeAt(i); + } + this.buffer[bidx][i] = 0; + + if (context === 1) { + const pdata = dbx.benchmark(this.buffer[bidx], i, 0, 0); + data = pdata; + } + else { + data = "output string"; + } + this.release_buffer(bidx); + + return data; + } + + benchmarkex(...args: any[]): string { + let offset = 0; + let request = { command: 0, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + let data = 0; + + if (this.init === 0) { + return data; + } + + let argc = args.length; + if (argc < 1) { + return data; + } + let command = 0; + if (argc === 1) { + request.command = DBX_CMND_GGET; + } + else if (argc === 3) { + request.command = DBX_CMND_GSET; + } + if (request.command === 0) { + return data; + } + + let bidx = this.get_buffer(); + offset = pack_arguments(this.buffer[bidx], offset, this.index, args, request, 0); + const pdata = dbx.benchmarkex(this.buffer[bidx], offset, request.command, 0); + get_result(this.buffer[bidx], pdata, request); + this.release_buffer(bidx); + + return request.result_data; + } + } + + class mglobal { + db: server; + global_name: string = ""; + base_buffer: Uint8Array; + base_offset: number = 0; + + constructor(db: server, ...args: any[]) { + let request = { command: 0, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + this.db = db; + this.base_buffer = new Uint8Array(DBX_INPUT_BUFFER_SIZE); + this.base_offset = 0; + this.base_offset = pack_arguments(this.base_buffer, this.base_offset, this.db.index, args, request, 0); + this.base_offset -= 5; + if (args.length > 0) { + this.global_name = args[0]; + } + return; + } + + set(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GSET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + get(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GGET, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + delete(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GDELETE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + defined(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GDEFINED, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + next(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GNEXT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + previous(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GPREVIOUS, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + increment(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GINCREMENT, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + lock(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + unlock(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_GUNLOCK, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + merge(...args: any[]): string { + let offset = 0; + let sort = 0; + let str = ""; + let request = { command: DBX_CMND_GMERGE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + + for (let argn = 0; argn < args.length; argn++) { + if (typeof args[argn] === "object" && args[argn].constructor.name == "mglobal") { + offset = block_copy(this.db.buffer[bidx], offset, args[argn].base_buffer, 15, args[argn].base_offset); + } + else if (typeof args[argn] === "string") { + str = args[argn]; + offset = block_add_string(this.db.buffer[bidx], offset, str, str.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + } + else { + str = args[argn].toString(); + offset = block_add_string(this.db.buffer[bidx], offset, str, str.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + } + } + + offset = block_add_string(this.db.buffer[bidx], offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR) + add_head(this.db.buffer[bidx], 0, offset, request.command); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + reset(...args: any[]) { + let request = { command: 0, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + this.base_offset = 0; + this.base_offset = pack_arguments(this.base_buffer, this.base_offset, this.db.index, args, request, 0); + this.base_offset -= 5; + if (args.length > 0) { + this.global_name = args[0]; + } + return; + } + + } + + class mclass { + db: server; + class_name: string = ""; + oref: string = ""; + base_buffer: Uint8Array; + base_offset: number = 0; + + constructor(db: server, ...args: any[]) { + this.db = db; + this.base_buffer = new Uint8Array(DBX_INPUT_BUFFER_SIZE); + this.base_offset = 0; + + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, db.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + if (args.length > 0) { + this.class_name = args[0]; + this.base_offset = block_add_string(this.base_buffer, this.base_offset, this.class_name, this.class_name.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + if (args.length > 1 && this.db.init > 0) { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + let bidx = this.db.get_buffer(); + + offset = pack_arguments(this.db.buffer[bidx], offset, db.index, args, request, 0); + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + this.oref = request.result_data; + } + } + } + return; + } + + classmethod(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, this.base_offset); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + this.oref = request.result_data; + } + return request.result_data; + } + + method(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_CMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + getproperty(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_CGETP, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + setproperty(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_CSETP, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + closeinstance(...args: any[]): string { + let offset = 0; + let request = { command: DBX_CMND_CCLOSE, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + + if (this.db.init === 0) { + return ""; + } + + let bidx = this.db.get_buffer(); + offset = block_copy(this.db.buffer[bidx], offset, this.base_buffer, 0, 15); + offset = block_add_string(this.db.buffer[bidx], offset, this.oref, this.oref.length, DBX_DSORT_DATA, DBX_DTYPE_OREF); + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 1); + if (request.async) { + async_command(this.db, this.db.buffer[bidx], offset, request, 0, args[request.argc]); + return null; + } + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + return request.result_data; + } + + reset(...args: any[]) { + this.base_offset = 0; + + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.base_buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + this.base_offset = block_add_size(this.base_buffer, this.base_offset, this.db.index, DBX_DSORT_DATA, DBX_DTYPE_INT); + + if (args.length > 0) { + this.class_name = args[0]; + this.base_offset = block_add_string(this.base_buffer, this.base_offset, this.class_name, this.class_name.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + if (args.length > 1 && this.db.init > 0) { + let offset = 0; + let request = { command: DBX_CMND_CCMETH, argc: 0, async: 0, result_data: "", error_message: "", type: 0 }; + let bidx = this.db.get_buffer(); + + offset = pack_arguments(this.db.buffer[bidx], offset, this.db.index, args, request, 0); + const pdata = dbx.command(this.db.buffer[bidx], offset, request.command, 0); + get_result(this.db.buffer[bidx], pdata, request); + this.db.release_buffer(bidx); + + if (result.type === DBX_DTYPE_OREF) { + this.oref = request.result_data; + } + } + } + return; + } + } + + async function async_command(db: server, buffer: Uint8Array, buffer_size: number, request: { command: number, argc: number, async: number, result_data: string, error_message: string, type: number }, context: number, callback: async_callback) { + let promise = new Promise((resolve, reject) => { + const pdata = dbx.command(buffer, buffer_size, request.command, context); + get_result(buffer, pdata, request); + db.release_buffer(0); + resolve(request.result_data); + }); + let result_data = await promise + if (request.error_message === "") { + callback(false, result_data); + } + else { + callback(true, result_data); + } + + return; + } + + + function pack_arguments(buffer: Uint8Array, offset: number, index: number, args: any[], request: { command: number, argc: number, async: number, result_data: string, error_message: string, type: number }, context: number): number { + let str = ""; + + if (context === 0) { + offset = block_add_size(buffer, offset, offset, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(buffer, offset, buffer.length, DBX_DSORT_DATA, DBX_DTYPE_INT); + offset = block_add_size(buffer, offset, index, DBX_DSORT_DATA, DBX_DTYPE_INT); + } + + request.argc = args.length; + if (request.argc > 1) { + if (typeof args[request.argc - 1] === "function") { + request.async = 1; + request.argc --; + } + } + + for (let argn = 0; argn < request.argc; argn ++) { + //console.log(argn, " = ", args[argn], " : ", typeof args[argn]); + if (typeof args[argn] === "string") + str = args[argn]; + else + str = args[argn].toString(); + if (argn == 0) + offset = block_add_string(buffer, offset, str, str.length, DBX_DSORT_GLOBAL, DBX_DTYPE_STR); + else + offset = block_add_string(buffer, offset, str, str.length, DBX_DSORT_DATA, DBX_DTYPE_STR); + } + + offset = block_add_string(buffer, offset, "", 0, DBX_DSORT_EOD, DBX_DTYPE_STR) + add_head(buffer, 0, offset, request.command); + + return offset; + } + + function get_result(pbuffer: Uint8Array, pdata: Uint8Array, request: { command: number, argc: number, async: number, result_data: string, error_message: string, type: number }): string { + let data_properties = { len: 0, type: 0, sort: 0 }; + + block_get_size(pbuffer, 0, data_properties); + //console.log("mg_dbx_napi.ts: data_view data properties => ", data_properties); + + if (data_properties.sort === DBX_DSORT_ERROR) { + if (data_properties.len === 0) { + request.error_message = "" + } + else { + request.error_message = pdata; + } + } + else { + if (data_properties.len === 0) { + request.result_data = "" + } + else { + request.result_data = pdata + } + } + request.type = data_properties.type; + return request.result_data; + } + + function block_copy(buffer_to: Uint8Array, offset: number, buffer_from: Uint8Array, from: number, to: number): number { + for (let i = from; i < to; i ++) { + buffer_to[offset ++] = buffer_from[i]; + } + return offset; + } + + function block_add_string(buffer: Uint8Array, offset: number, data:string, data_len: number, data_sort: number, data_type: number): number { + offset = block_add_size(buffer, offset, data_len, data_sort, data_type); + for (let i = 0; i < data_len; i ++) { + buffer[offset ++] = data.charCodeAt(i); + } + return offset; + } + + function block_add_size(buffer: Uint8Array, offset: number, data_len: number, data_sort: number, data_type: number): number { + buffer[offset + 0] = (data_len >> 0); + buffer[offset + 1] = (data_len >> 8); + buffer[offset + 2] = (data_len >> 16); + buffer[offset + 3] = (data_len >> 24); + buffer[offset + 4] = ((data_sort * 20) + data_type); + return (offset + 5); + } + + function add_head(buffer: Uint8Array, offset: number, data_len: number, cmnd: number): number { + buffer[offset + 0] = (data_len >> 0); + buffer[offset + 1] = (data_len >> 8); + buffer[offset + 2] = (data_len >> 16); + buffer[offset + 3] = (data_len >> 24); + buffer[offset + 4] = cmnd; + return (offset + 5); + } + + function block_get_size(buffer: Uint8Array, offset: number, data_properties: {len: number; type: number; sort: number}) { + data_properties.len = ((buffer[offset + 0]) | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); + data_properties.sort = buffer[4]; + data_properties.type = data_properties.sort % 20; + data_properties.sort = Math.floor(data_properties.sort / 20); + return data_properties; + } + + return { + server, + mglobal, + mclass + }; +}; +export {mgdbx}; From bd5639afe15196a215b0957f6c42d25f74fcbf54 Mon Sep 17 00:00:00 2001 From: robtweed Date: Fri, 20 Oct 2023 11:09:41 +0100 Subject: [PATCH 11/12] Improved child process stop signalling --- experimental/mg_web_node.mjs | 79 +++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/experimental/mg_web_node.mjs b/experimental/mg_web_node.mjs index 9ecce56..8ad1abc 100644 --- a/experimental/mg_web_node.mjs +++ b/experimental/mg_web_node.mjs @@ -52,7 +52,26 @@ if (process.argv.length > 3) { if (primary) { console.log('mg_web server for Node.js %s; CPUs=%d; pid=%d;', process.version, cpus, process.pid); - let server = net.createServer(); + let server = net.createServer(); + let workers = new Map(); + + process.on( 'SIGINT', async function() { + console.log('*** CTRL & C detected: shutting down gracefully...'); + + if (workers.size > 0) { + for (const [key, worker] of workers) { + console.log('signalling worker ' + worker.pid + ' to stop: key = ' + key); + worker.send('<>'); + workers.delete(key); + } + setTimeout(() => { + process.exit(); + }, 1000); + } + else { + process.exit(); + } + }); server.on('connection', (conn) => { let remote_address = conn.remoteAddress + ':' + conn.remotePort; @@ -60,10 +79,20 @@ if (primary) { conn.on('data', (d) => { let worker = child_process.fork(mod_name, [1000000], { stdio: ['inherit', 'inherit', 'inherit', 'ipc'] }); + + workers.set(worker.pid, worker); + worker.on('message', message => { if (message === 'ready!') { worker.send(d, conn); + return; + } + + if (message === 'stopping') { + console.log('master process removing stopped worker from Map'); + workers.delete(worker.pid); } + }); }); }); @@ -74,6 +103,17 @@ if (primary) { } else { + process.on( 'SIGINT', function() { + console.log('*** CTRL & C detected in worker'); + }); + + process.on( 'SIGTERM', function() { + console.log('*** SIGTERM detected in worker'); + }); + + let socket; + const evTarget = new EventTarget(); + let data_properties = { len: 0, type: 0, sort: 0 }; let buffer = new Uint8Array(2048); let handlers = new Map(); @@ -136,6 +176,20 @@ else { process.on('message', (dbx, conn) => { + //console.log('child process ' + process.pid + ' received:'); + //console.log(dbx); + + if (conn && !socket) socket = conn; + + if (dbx === '<>') { + console.log('stop child process ' + process.pid); + evTarget.dispatchEvent(new Event('stop')); + setTimeout(() => { + process.exit(); + }, 1000); + return; + } + let remote_address = conn.remoteAddress + ':' + conn.remotePort; console.log('mg_web new worker process created pid=%d; client=%s', process.pid, remote_address); @@ -206,8 +260,15 @@ else { offset += 5; if (data_properties.sort === 5) { // CGI environment variable - let d = data.slice(offset, offset + len).toString().split("="); - cgi.set(d[0], d[1]); + let d = data.slice(offset, offset + len).toString(); + if (d.startsWith('QUERY_STRING=')) { + d = d.split('QUERY_STRING=')[1]; + cgi.set('QUERY_STRING', d); + } + else { + d = d.split("="); + cgi.set(d[0], d[1]); + } } if (data_properties.sort === 6) { // request payload (if any) @@ -261,6 +322,7 @@ else { try { let fn = handlers.get(fun); sys.set('socket', conn); + sys.set('evTarget', evTarget); if (fn.constructor.name === 'AsyncFunction') { res = await fn(cgi, content, sys); } @@ -283,9 +345,14 @@ else { }); - //conn.once('close', () => { - // console.log('connection closed'); - //}); + conn.on('close', () => { + console.log('connection closed'); + process.send('stopping'); + evTarget.dispatchEvent(new Event('stop')); + setTimeout(() => { + process.exit(); + }, 1000); + }); conn.on('error', (err) => { console.log('Connection error: %s', err.message); From afac219fa101391889cff3f00d439b9c80eade87 Mon Sep 17 00:00:00 2001 From: robtweed Date: Sun, 22 Oct 2023 10:32:13 +0100 Subject: [PATCH 12/12] Update mg_web_node.mjs