@sassoftware/esp-connect
Version:
Package used to connect to an ESP server (version 6.2+)
816 lines (702 loc) • 17 kB
JavaScript
/*
Copyright © 2020, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
class Bitset
{
constructor(size)
{
this._size = size;
this._bits = new Uint8Array(size);
}
get value()
{
var value = 0;
for (var i = 0; i < this._size; i++)
{
if (this._bits[i] == 1)
{
//value |= (1 << i);
value |= (1 << (this._size - 1 - i));
}
}
return(value);
}
set(index)
{
if (index < this._size)
{
this._bits[this._size - 1 - index] = 1;
}
}
unset(index)
{
if (index < this._size)
{
this._bits[index] = 0;
}
}
setBits(value)
{
for (var i = this._size - 1; i >= 0; i--)
{
if (value & (1 << i))
{
//this.set(this._size - 1 - i);
this.set(i);
}
}
}
to_string()
{
var s = "";
for (var i = 0; i < this._size; i++)
{
if (this._bits[i] == 0)
{
s += "0";
}
else
{
s += "1";
}
}
return(s);
}
}
class JsonEncoder
{
constructor(o)
{
this._size = this.size(o) * 2;
this._data = new ArrayBuffer(this._size);
this._view = new DataView(this._data);
this._index = 0;
this._debug = false;
this.encode(o);
}
get data()
{
var data = (this._index < this._size) ? this._data.slice(0,this._index) : this_data;
return(data);
}
encode(o,name)
{
if (o instanceof Object)
{
if (Array.isArray(o))
{
if (name != null)
{
this.writeString(name);
}
this.beginArray();
const self = this;
o.forEach(item =>
{
self.encode(item);
});
this.endArray();
}
else if (o instanceof ArrayBuffer)
{
this.writeBuffer(o,name);
}
else
{
if (name != null)
{
this.writeString(name);
}
this.beginObject();
for (var x in o)
{
this.encode(o[x],x);
}
this.endObject();
}
}
else
{
this.writeValue(o,name);
}
}
beginArray()
{
var bits = new Bitset(8);
bits.setBits(31);
bits.set(7);
this._view.setUint8(this._index,bits.value);
this._index += 1;
}
endArray()
{
this.writeStopCode();
}
beginObject()
{
var bits = new Bitset(8);
bits.setBits(31);
bits.set(5);
bits.set(7);
this._view.setUint8(this._index,bits.value);
this._index += 1;
}
endObject()
{
this.writeStopCode();
}
writeStopCode()
{
var bits = new Bitset(8);
bits.setBits(31);
bits.set(5);
bits.set(6);
bits.set(7);
this._view.setUint8(this._index,bits.value);
this._index += 1;
}
writeValue(value,name)
{
if (isNaN(value) == false)
{
if (Number.isInteger(value))
{
//this.writeI64(value,name);
this.writeI32(value,name);
}
else
{
this.writeDouble(value,name);
}
}
else
{
var type = typeof value;
if (type == "string")
{
this.writeString(value,name);
}
else if (type == "boolean")
{
this.writeBool(value,name);
}
}
}
writeString(value,name)
{
if (name != null && name.length > 0)
{
this.writeString(name,null);
}
var info = 0;
if (value.length < 24)
{
var bits = new Bitset(8);
bits.setBits(value.length);
bits.set(5);
bits.set(6);
this._view.setInt8(this._index,bits.value);
this._index += 1;
}
else
{
var bits = new Bitset(8);
bits.set(1);
bits.set(3);
bits.set(4);
bits.set(5);
bits.set(6);
this._view.setInt8(this._index,bits.value);
this._index += 1;
this._view.setInt32(this._index,value.length);
this._index += 4;
}
for (var i = 0; i < value.length; i++)
{
this._view.setUint8(this._index,value.charCodeAt(i));
this._index++;
}
}
writeI8(value,name)
{
if (name != null && name.length > 0)
{
this.writeString(name,null);
}
var negative = value < 0;
if (negative)
{
value = -1 - value;
}
if (value >= 24)
{
var bits = new Bitset(8);
bits.set(3);
bits.set(4);
if (negative)
{
bits.set(5);
}
this._view.setInt8(this._index,bits.value);
}
else if (negative)
{
var bits = new Bitset(8);
bits.setBits(value);
bits.set(5);
this._view.setInt8(this._index,bits.value);
}
else
{
this._view.setInt8(this._index,value);
}
this._index += 1;
}
writeI32(value,name)
{
if (name != null && name.length > 0)
{
this.writeString(name,null);
}
var negative = value < 0;
if (negative)
{
value = -1 - value;
}
var bits = new Bitset(8);
bits.set(1);
bits.set(3);
bits.set(4);
if (negative)
{
bits.set(5);
}
this._view.setInt8(this._index,bits.value);
this._index += 1;
this._view.setInt32(this._index,value);
this._index += 4;
}
writeI64(value,name)
{
if (name != null && name.length > 0)
{
this.writeString(name,null);
}
var negative = value < 0;
if (negative)
{
value = -1 - value;
}
var bits = new Bitset(8);
bits.set(0);
bits.set(1);
bits.set(3);
bits.set(4);
if (negative)
{
bits.set(5);
}
this._view.setInt8(this._index,bits.value);
this._index += 1;
this._view.setBigInt64(this._index,value);
this._index += 8;
}
writeDouble(value,name)
{
if (name != null && name.length > 0)
{
this.writeString(name,null);
}
var bits = new Bitset(8);
bits.set(0);
bits.set(1);
bits.set(3);
bits.set(4);
bits.set(5);
bits.set(6);
bits.set(7);
this._view.setUint8(this._index,bits.value);
this._index += 1;
this._view.setFloat64(this._index,value);
this._index += 8;
}
size(object)
{
var bytes = 0;
var type = typeof object;
if (type == "string")
{
bytes = object.length * 2;
}
else if (type == "number")
{
bytes = 8;
}
else if (type == "bigint")
{
bytes = 8;
}
else if (type == "boolean")
{
bytes = 4;
}
else if (type == "object")
{
if (object instanceof ArrayBuffer)
{
bytes = object.byteLength;
}
else
{
for (var x in object)
{
bytes += this.size(object[x]);
}
}
}
else if (type == "array")
{
object.forEach(item=>
{
bytes += this.size(item);
});
}
else
{
throw("unknown type: " + type);
}
bytes += 5;
return(bytes);
}
}
class JsonDecoder
{
constructor(data)
{
this._data = new DataView(data,0);
this._size = this._data.buffer.byteLength;
this._index = 0;
this._o = null;
this._debug = false;
this.addTo();
var type;
var info;
var header = this.getHeader();
this.addTo(header.type,header.info,null);
}
addTo(type,info,to)
{
if (type == 5)
{
this.addObject(info,to);
}
else if (type == 4)
{
this.addArray(info,to);
}
else
{
this.addValue(type,info,"",to);
}
}
addArray(info,to)
{
if (this._o == null)
{
this._o = [];
to = this._o;
}
var size = (info != 31) ? this.getUnsigned(info) : null;
var header;
while ((header = this.getHeader()) != null)
{
if (size == null)
{
if (header.type == 7 && header.info == 31)
{
break;
}
}
if (header.type == 4)
{
if (Array.isArray(to))
{
var o = [];
this.addTo(header.type,header.info,o);
to.push(o);
}
}
else if (header.type == 5)
{
if (Array.isArray(to))
{
var o = {};
this.addTo(header.type,header.info,o);
to.push(o);
}
}
else
{
this.addTo(header.type,header.info,to);
}
if (size != null)
{
if (--size == 0)
{
break;
}
}
}
}
addObject(info,to)
{
if (this._o == null)
{
this._o = {};
to = this._o;
}
var size = (info != 31) ? this.getUnsigned(info) : null;
var name = null;
var header;
while ((header = this.getHeader()) != null)
{
if (size == null)
{
if (header.type == 7 && header.info == 31)
{
break;
}
}
if (header.type != 3)
{
console.log("====== bad");
break;
}
name = this.getString(header.info);
header = this.getHeader();
if (header.type == 4)
{
to[name] = [];
this.addTo(header.type,header.info,to[name]);
}
else if (header.type == 5)
{
to[name] = {}
this.addTo(header.type,header.info,to[name]);
}
else
{
this.addValue(header.type,header.info,name,to);
}
if (size != null)
{
if (--size == 0)
{
break;
}
}
}
}
addValue(type,info,name,to)
{
var value = null;
if (type == 0 || type == 1 || type == 2 || type == 3 || type == 7)
{
if (type == 0)
{
value = this.getUnsigned(info);
}
else if (type == 1)
{
var tmp = this.getUnsigned(info);
var value = -1 - tmp;
}
else if (type == 2)
{
var size = this.getUnsigned(info);
if (size > 0)
{
value = new Uint8Array(this._data.buffer,this._index,size);
if (_api._isNode)
{
value = Buffer.from(value).toString("base64");
}
else
{
value = btoa(value.reduce(
function (data,byte) {
return(data + String.fromCharCode(byte));
},
''));
}
this._index += size;
}
}
else if (type == 3)
{
value = this.getString(info);
}
else if (type == 7)
{
value = this.getDouble(info);
}
}
if (value != null)
{
if (this._o == null)
{
this._o = value;
}
else if (Array.isArray(to))
{
to.push(value);
}
else
{
to[name] = value;
}
}
}
getHeader()
{
if (this._index >= this._size)
{
return(null);
}
var header = this._data.getUint8(this._index);
var o = {};
o.type = (header & 0xe0) >> 5;
o.info = (header & 0x1f);
/*
console.log(JSON.stringify(o));
*/
this._index++;
return(o);
}
getString(info)
{
var s = null;
var size = this.getUnsigned(info);
if (size > 0)
{
var data = new Uint8Array(this._data.buffer,this._index,size);
s = data.reduce(
function (data,byte) {
return(data + String.fromCharCode(byte));
},
'');
this._index += size;
}
return(s);
}
/*
getString(info)
{
var s = null;
var size = this.getUnsigned(info);
if (size > 0)
{
s = String.fromCharCode.apply(null,new Uint8Array(this._data.buffer,this._index,size));
this._index += size;
}
return(s);
}
*/
getUnsigned(info)
{
var value = 0;
if (info < 24)
{
value = info;
}
else if (info <= 27)
{
if (info == 24)
{
value = this._data.getUint8(this._index);
this._index += 1;
}
else if (info == 25)
{
value = this._data.getUint16(this._index);
this._index += 2;
}
else if (info == 26)
{
value = this._data.getUint32(this._index);
this._index += 4;
}
else if (info == 27)
{
value = this._data.getBigInt64(this._index);
this._index += 8;
}
}
return(value);
}
getDouble(info)
{
var value = 0.0;
if (info < 25)
{
if (info < 24)
{
value = info;
}
else
{
this._index += 1;
}
}
else if (info == 25)
{
this._index += 2;
}
else if (info == 26)
{
value = this._data.getFloat32(this._index);
this._index += 4;
}
else if (info == 27)
{
value = this._data.getFloat64(this._index);
this._index += 8;
}
return(value);
}
}
var _api =
{
_isNode:false,
_supportsGetBigInt:false,
init:function()
{
var buf = new ArrayBuffer(2);
var dv = new DataView(buf);
dv.setInt16(0,256,true);
this._supportsGetBigInt = (("getBigInt64" in dv) && typeof(dv["getBigInt64"]) == "function");
if (typeof process === "object")
{
if (process.hasOwnProperty("versions"))
{
if (process.versions.hasOwnProperty("node"))
{
this._isNode = true;
}
}
}
},
encode:function(o)
{
var encoder = new JsonEncoder(o);
return(encoder.data);
},
decode:function(data)
{
var data = new JsonDecoder(data);
return(data._o);
}
};
_api.init();
export {_api as codec};