Integrate Buffer class into JS codec runtime.

This commit is contained in:
Orne Brocaar 2022-05-10 14:08:47 +01:00
parent a656b9f3a1
commit 5b1d70cdbf
6 changed files with 2430 additions and 1 deletions

7
Cargo.lock generated

@ -2905,6 +2905,12 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "relative-path"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4e112eddc95bbf25365df3b5414354ad2fe7ee465eddb9965a515faf8c3b6d9"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@ -2985,6 +2991,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6da4f2b300a58f9fb368bb68b21e288586e503cc0e0c3c3f94af9b28a717e2c"
dependencies = [
"relative-path",
"rquickjs-sys",
]

@ -89,7 +89,7 @@ paho-mqtt = { version = "0.9", features = ["vendored-ssl"] }
hex = "0.4"
# Codecs
rquickjs = { version = "0.1.5", features = ["bindgen"] }
rquickjs = { version = "0.1.5", features = ["bindgen", "loader", "array-buffer"] }
# Misc
lazy_static = "1.4"

@ -7,6 +7,10 @@ use rquickjs::IntoJs;
use super::convert;
use crate::config;
mod vendor_base64_js;
mod vendor_buffer;
mod vendor_ieee754;
pub async fn decode(
f_port: u8,
variables: &HashMap<String, String>,
@ -16,13 +20,25 @@ pub async fn decode(
let conf = config::get();
let max_run_ts = SystemTime::now() + conf.codec.js.max_execution_time;
let resolver = rquickjs::BuiltinResolver::default()
.with_module("base64-js")
.with_module("ieee754")
.with_module("buffer");
let loader = rquickjs::BuiltinLoader::default()
.with_module("base64-js", vendor_base64_js::SCRIPT)
.with_module("ieee754", vendor_ieee754::SCRIPT)
.with_module("buffer", vendor_buffer::SCRIPT);
let rt = rquickjs::Runtime::new()?;
rt.set_interrupt_handler(Some(Box::new(move || SystemTime::now() > max_run_ts)));
rt.set_loader(resolver, loader);
let ctx = rquickjs::Context::full(&rt)?;
let script = format!(
r#"
import {{ Buffer }} from "buffer";
{}
export {{ decodeUplink }};
@ -61,14 +77,27 @@ pub async fn encode(
let conf = config::get();
let max_run_ts = SystemTime::now() + conf.codec.js.max_execution_time;
let resolver = rquickjs::BuiltinResolver::default()
.with_module("base64-js")
.with_module("ieee754")
.with_module("buffer");
let loader = rquickjs::BuiltinLoader::default()
.with_module("base64-js", vendor_base64_js::SCRIPT)
.with_module("ieee754", vendor_ieee754::SCRIPT)
.with_module("buffer", vendor_buffer::SCRIPT);
let rt = rquickjs::Runtime::new()?;
rt.set_interrupt_handler(Some(Box::new(move || SystemTime::now() > max_run_ts)));
rt.set_loader(resolver, loader);
let ctx = rquickjs::Context::full(&rt)?;
let script = format!(
r#"
import {{ Buffer }} from "buffer";
{}
export {{ encodeDownlink }};
"#,
encode_config,
@ -113,10 +142,13 @@ pub mod test {
pub async fn test_decode() {
let decoder = r#"
function decodeUplink(input) {
var buff = new Buffer(input.bytes);
return {
object: {
f_port: input.fPort,
variables: input.variables,
data_hex: buff.toString('hex'),
data: input.bytes
}
};
@ -159,6 +191,12 @@ pub mod test {
)),
},
),
(
"data_hex".to_string(),
pbjson_types::Value {
kind: Some(pbjson_types::value::Kind::StringValue("010203".to_string())),
},
),
(
"data".to_string(),
pbjson_types::Value {

@ -0,0 +1,155 @@
// base64-js does basic base64 encoding/decoding in pure JS.
//
// License: MIT
// Source: https://github.com/beatgammit/base64-js
pub const SCRIPT: &str = r#"
'use strict'
export { byteLength, toByteArray, fromByteArray };
var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
lookup[i] = code[i]
revLookup[code.charCodeAt(i)] = i
}
// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63
function getLens (b64) {
var len = b64.length
if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// Trim off extra bytes after placeholder bytes are found
// See: https://github.com/beatgammit/base64-js/issues/42
var validLen = b64.indexOf('=')
if (validLen === -1) validLen = len
var placeHoldersLen = validLen === len
? 0
: 4 - (validLen % 4)
return [validLen, placeHoldersLen]
}
// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function _byteLength (b64, validLen, placeHoldersLen) {
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}
function toByteArray (b64) {
var tmp
var lens = getLens(b64)
var validLen = lens[0]
var placeHoldersLen = lens[1]
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
var curByte = 0
// if there are placeholders, only get up to the last complete 4 chars
var len = placeHoldersLen > 0
? validLen - 4
: validLen
var i
for (i = 0; i < len; i += 4) {
tmp =
(revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)]
arr[curByte++] = (tmp >> 16) & 0xFF
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 2) {
tmp =
(revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 1) {
tmp =
(revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
return arr
}
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] +
lookup[num >> 12 & 0x3F] +
lookup[num >> 6 & 0x3F] +
lookup[num & 0x3F]
}
function encodeChunk (uint8, start, end) {
var tmp
var output = []
for (var i = start; i < end; i += 3) {
tmp =
((uint8[i] << 16) & 0xFF0000) +
((uint8[i + 1] << 8) & 0xFF00) +
(uint8[i + 2] & 0xFF)
output.push(tripletToBase64(tmp))
}
return output.join('')
}
function fromByteArray (uint8) {
var tmp
var len = uint8.length
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
var parts = []
var maxChunkLength = 16383 // must be multiple of 3
// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
}
// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1]
parts.push(
lookup[tmp >> 2] +
lookup[(tmp << 4) & 0x3F] +
'=='
)
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
parts.push(
lookup[tmp >> 10] +
lookup[(tmp >> 4) & 0x3F] +
lookup[(tmp << 2) & 0x3F] +
'='
)
}
return parts.join('')
}
"#;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,110 @@
// Read/write IEEE754 floating point numbers from/to a Buffer or array-like object.
//
// License: BSD (3 clause)
// Source: https://github.com/feross/ieee754
pub const SCRIPT: &str = r#"
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
export function read(buffer, offset, isLE, mLen, nBytes) {
let e, m
const eLen = (nBytes * 8) - mLen - 1
const eMax = (1 << eLen) - 1
const eBias = eMax >> 1
let nBits = -7
let i = isLE ? (nBytes - 1) : 0
const d = isLE ? -1 : 1
let s = buffer[offset + i]
i += d
e = s & ((1 << (-nBits)) - 1)
s >>= (-nBits)
nBits += eLen
while (nBits > 0) {
e = (e * 256) + buffer[offset + i]
i += d
nBits -= 8
}
m = e & ((1 << (-nBits)) - 1)
e >>= (-nBits)
nBits += mLen
while (nBits > 0) {
m = (m * 256) + buffer[offset + i]
i += d
nBits -= 8
}
if (e === 0) {
e = 1 - eBias
} else if (e === eMax) {
return m ? NaN : ((s ? -1 : 1) * Infinity)
} else {
m = m + Math.pow(2, mLen)
e = e - eBias
}
return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
}
export function write(buffer, value, offset, isLE, mLen, nBytes) {
let e, m, c
let eLen = (nBytes * 8) - mLen - 1
const eMax = (1 << eLen) - 1
const eBias = eMax >> 1
const rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
let i = isLE ? 0 : (nBytes - 1)
const d = isLE ? 1 : -1
const s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
value = Math.abs(value)
if (isNaN(value) || value === Infinity) {
m = isNaN(value) ? 1 : 0
e = eMax
} else {
e = Math.floor(Math.log(value) / Math.LN2)
if (value * (c = Math.pow(2, -e)) < 1) {
e--
c *= 2
}
if (e + eBias >= 1) {
value += rt / c
} else {
value += rt * Math.pow(2, 1 - eBias)
}
if (value * c >= 2) {
e++
c /= 2
}
if (e + eBias >= eMax) {
m = 0
e = eMax
} else if (e + eBias >= 1) {
m = ((value * c) - 1) * Math.pow(2, mLen)
e = e + eBias
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
e = 0
}
}
while (mLen >= 8) {
buffer[offset + i] = m & 0xff
i += d
m /= 256
mLen -= 8
}
e = (e << mLen) | m
eLen += mLen
while (eLen > 0) {
buffer[offset + i] = e & 0xff
i += d
e /= 256
eLen -= 8
}
buffer[offset + i - d] |= s * 128
}
"#;