mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-18 23:38:20 +00:00
Integrate Buffer class into JS codec runtime.
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2905,6 +2905,12 @@ version = "0.6.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "relative-path"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4e112eddc95bbf25365df3b5414354ad2fe7ee465eddb9965a515faf8c3b6d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@ -2985,6 +2991,7 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6da4f2b300a58f9fb368bb68b21e288586e503cc0e0c3c3f94af9b28a717e2c"
|
checksum = "c6da4f2b300a58f9fb368bb68b21e288586e503cc0e0c3c3f94af9b28a717e2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"relative-path",
|
||||||
"rquickjs-sys",
|
"rquickjs-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ paho-mqtt = { version = "0.9", features = ["vendored-ssl"] }
|
|||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
|
||||||
# Codecs
|
# Codecs
|
||||||
rquickjs = { version = "0.1.5", features = ["bindgen"] }
|
rquickjs = { version = "0.1.5", features = ["bindgen", "loader", "array-buffer"] }
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
@ -7,6 +7,10 @@ use rquickjs::IntoJs;
|
|||||||
use super::convert;
|
use super::convert;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
|
mod vendor_base64_js;
|
||||||
|
mod vendor_buffer;
|
||||||
|
mod vendor_ieee754;
|
||||||
|
|
||||||
pub async fn decode(
|
pub async fn decode(
|
||||||
f_port: u8,
|
f_port: u8,
|
||||||
variables: &HashMap<String, String>,
|
variables: &HashMap<String, String>,
|
||||||
@ -16,13 +20,25 @@ pub async fn decode(
|
|||||||
let conf = config::get();
|
let conf = config::get();
|
||||||
let max_run_ts = SystemTime::now() + conf.codec.js.max_execution_time;
|
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()?;
|
let rt = rquickjs::Runtime::new()?;
|
||||||
rt.set_interrupt_handler(Some(Box::new(move || SystemTime::now() > max_run_ts)));
|
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 ctx = rquickjs::Context::full(&rt)?;
|
||||||
|
|
||||||
let script = format!(
|
let script = format!(
|
||||||
r#"
|
r#"
|
||||||
|
import {{ Buffer }} from "buffer";
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
export {{ decodeUplink }};
|
export {{ decodeUplink }};
|
||||||
@ -61,14 +77,27 @@ pub async fn encode(
|
|||||||
let conf = config::get();
|
let conf = config::get();
|
||||||
let max_run_ts = SystemTime::now() + conf.codec.js.max_execution_time;
|
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()?;
|
let rt = rquickjs::Runtime::new()?;
|
||||||
rt.set_interrupt_handler(Some(Box::new(move || SystemTime::now() > max_run_ts)));
|
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 ctx = rquickjs::Context::full(&rt)?;
|
||||||
|
|
||||||
let script = format!(
|
let script = format!(
|
||||||
r#"
|
r#"
|
||||||
|
import {{ Buffer }} from "buffer";
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
export {{ encodeDownlink }};
|
export {{ encodeDownlink }};
|
||||||
"#,
|
"#,
|
||||||
encode_config,
|
encode_config,
|
||||||
@ -113,10 +142,13 @@ pub mod test {
|
|||||||
pub async fn test_decode() {
|
pub async fn test_decode() {
|
||||||
let decoder = r#"
|
let decoder = r#"
|
||||||
function decodeUplink(input) {
|
function decodeUplink(input) {
|
||||||
|
var buff = new Buffer(input.bytes);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
object: {
|
object: {
|
||||||
f_port: input.fPort,
|
f_port: input.fPort,
|
||||||
variables: input.variables,
|
variables: input.variables,
|
||||||
|
data_hex: buff.toString('hex'),
|
||||||
data: input.bytes
|
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(),
|
"data".to_string(),
|
||||||
pbjson_types::Value {
|
pbjson_types::Value {
|
155
chirpstack/src/codec/js/vendor_base64_js.rs
Normal file
155
chirpstack/src/codec/js/vendor_base64_js.rs
Normal file
@ -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('')
|
||||||
|
}
|
||||||
|
"#;
|
2119
chirpstack/src/codec/js/vendor_buffer.rs
Normal file
2119
chirpstack/src/codec/js/vendor_buffer.rs
Normal file
File diff suppressed because it is too large
Load Diff
110
chirpstack/src/codec/js/vendor_ieee754.rs
Normal file
110
chirpstack/src/codec/js/vendor_ieee754.rs
Normal file
@ -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
|
||||||
|
}
|
||||||
|
"#;
|
Reference in New Issue
Block a user