aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/html/compactbinary.js
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2024-10-03 13:54:59 +0200
committerGitHub Enterprise <[email protected]>2024-10-03 13:54:59 +0200
commit61ec0fbdf3f87a41486e5d5dfde5d23e91941b42 (patch)
tree66775461556506cd39c84256efe3f119c29fecfc /src/zenserver/frontend/html/compactbinary.js
parentcache get command (#183) (diff)
downloadzen-61ec0fbdf3f87a41486e5d5dfde5d23e91941b42.tar.xz
zen-61ec0fbdf3f87a41486e5d5dfde5d23e91941b42.zip
- Improvement: Self-hosted dashboard (#181)
Self-hosted dashboard gets oplog entry view and a stats browser
Diffstat (limited to 'src/zenserver/frontend/html/compactbinary.js')
-rw-r--r--src/zenserver/frontend/html/compactbinary.js440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/compactbinary.js b/src/zenserver/frontend/html/compactbinary.js
new file mode 100644
index 000000000..c648e6052
--- /dev/null
+++ b/src/zenserver/frontend/html/compactbinary.js
@@ -0,0 +1,440 @@
+"use strict";
+
+////////////////////////////////////////////////////////////////////////////////
+class VarInt
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+VarInt.measure = function(data_view)
+{
+ var value = data_view[0];
+ var ret = 1;
+ for (; value & 0x80; value <<= 1, ++ret);
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+VarInt.read_uint = function(data_view, return_type=Number)
+{
+ const length = VarInt.measure(data_view);
+ var value = return_type(data_view[0] & (0xff >> length));
+ for (var i = 1; i < length; ++i)
+ {
+ value <<= return_type(8);
+ value |= return_type(data_view[i]);
+ }
+ return [value, length];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+VarInt.read_int = function(data_view, return_type=Number)
+{
+ var [value, length] = VarInt.read_uint(data_view, return_type);
+ value = -(value & return_type(1)) ^ (value >> return_type(1));
+ return [value, length];
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+function cb_assert(expr_result)
+{
+ if (Boolean(expr_result) == false)
+ throw Error("compactbinary error");
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+const CbFieldType = {
+ None : 0x00,
+ Null : 0x01,
+ Object : 0x02,
+ UniformObject : 0x03,
+ Array : 0x04,
+ UniformArray : 0x05,
+ Binary : 0x06,
+ String : 0x07,
+ IntegerPositive : 0x08,
+ IntegerNegative : 0x09,
+ Float32 : 0x0a,
+ Float64 : 0x0b,
+ BoolFalse : 0x0c,
+ BoolTrue : 0x0d,
+ ObjectAttachment : 0x0e,
+ BinaryAttachment : 0x0f,
+ Hash : 0x10,
+ Uuid : 0x11,
+ DateTime : 0x12,
+ TimeSpan : 0x13,
+ ObjectId : 0x14,
+ CustomById : 0x1e,
+ CustomByName : 0x1f,
+ Reserved : 0x20,
+ HasFieldType : 0x40,
+ HasFieldName : 0x80,
+}
+
+////////////////////////////////////////////////////////////////////////////////
+class CbFieldTypeOps
+{
+ static SerializedTypeMask = 0b10111111;
+ static TypeMask = 0b00111111;
+ static ObjectMask = 0b00111110;
+ static ObjectBase = 0b00000010;
+ static ArrayMask = 0b00111110;
+ static ArrayBase = 0b00000100;
+ static IntegerMask = 0b00111110;
+ static IntegerBase = 0b00001000;
+ static FloatMask = 0b00111100;
+ static FloatBase = 0b00001000;
+ static BoolMask = 0b00111110;
+ static BoolBase = 0b00001100;
+ static AttachmentMask = 0b00111110;
+ static AttachmentBase = 0b00001110;
+
+ static get_type(type) { return type & CbFieldTypeOps.TypeMask; }
+ static get_serialized_type(type) { return type & CbFieldTypeOps.SerializedTypeMask; }
+ static has_field_type(type) { return (type & CbFieldType.HasFieldType) != 0; }
+ static has_field_name(type) { return (type & CbFieldType.HasFieldName) != 0; }
+ static is_none(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.None; }
+ static is_null(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.Null; }
+ static is_object(type) { return (type & CbFieldTypeOps.ObjectMask) == CbFieldTypeOps.ObjectBase; }
+ static is_array(type) { return (type & CbFieldTypeOps.ArrayMask) == CbFieldTypeOps.ArrayBase; }
+ static is_binary(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.Binary; }
+ static is_string(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.String; }
+ static is_integer(type) { return (type & CbFieldTypeOps.IntegerMask) == CbFieldTypeOps.IntegerBase; }
+ static is_float(type) { return (type & CbFieldTypeOps.FloatMask) == CbFieldTypeOps.FloatBase; }
+ static is_bool(type) { return (type & CbFieldTypeOps.BoolMask) == CbFieldTypeOps.BoolBase; }
+ static is_object_attachment(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.ObjectAttachment; }
+ static is_binary_attachment(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.BinaryAttachment; }
+ static is_attachment(type) { return (type & CbFieldTypeOps.AttachmentMask) == CbFieldTypeOps.AttachmentBase; }
+ static is_uuid(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.Uuid; }
+ static is_object_id(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.ObjectId; }
+ static is_custom_by_id(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.CustomById; }
+ static is_custom_by_name(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.CustomByName; }
+ static is_date_time(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.DateTime; }
+ static is_time_span(type) { return CbFieldTypeOps.get_type(type) == CbFieldType.TimeSpan; }
+ static is_hash(type) { var t = CbFieldTypeOps.get_type(type); return t >= CbFieldType.ObjectAttachment && t <= CbFieldType.Hash; }
+ static may_contain_attachments(type){ var t = CbFieldTypeOps.get_type(type); return is_object(t) || is_array(t) || is_attachement(t); }
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class CbFieldView
+{
+ constructor()
+ {
+ this._type = CbFieldType.None;
+ this._name = "";
+ this._data_view = undefined;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype._from_field = function(field)
+{
+ this._type = field._type;
+ this._name = field._name;
+ this._data_view = field._data_view;
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype._from_data = function(data_view, type=CbFieldType.HasFieldType)
+{
+ if (CbFieldTypeOps.has_field_type(type))
+ {
+ type = data_view[0] | CbFieldType.HasFieldType;
+ data_view = data_view.subarray(1);
+ }
+
+ if (CbFieldTypeOps.has_field_name(type))
+ {
+ const [n, varint_len] = VarInt.read_uint(data_view);
+ this._name = new TextDecoder().decode(data_view.subarray(varint_len, n + varint_len));
+ data_view = data_view.subarray(n + varint_len);
+ }
+
+ this._type = type;
+ this._data_view = data_view;
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView._iterate = function*(data_view, uniform_type)
+{
+ while (data_view.length > 0)
+ {
+ const field = new CbFieldView()._from_data(data_view, uniform_type);
+ yield field;
+
+ const field_size = field.get_payload_size();
+ cb_assert(field_size <= data_view.length);
+ data_view = field.get_payload().subarray(field_size);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.get_type = function()
+{
+ return this._type;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.get_name = function()
+{
+ return this._name;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.get_payload = function()
+{
+ return this._data_view;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.get_payload_size = function()
+{
+ switch (CbFieldTypeOps.get_type(this.get_type()))
+ {
+ case CbFieldType.None:
+ case CbFieldType.Null:
+ return 0;
+ case CbFieldType.Object:
+ case CbFieldType.UniformObject:
+ case CbFieldType.Array:
+ case CbFieldType.UniformArray:
+ case CbFieldType.Binary:
+ case CbFieldType.String:
+ const [value, varint_len] = VarInt.read_uint(this._data_view);
+ return value + varint_len;
+ case CbFieldType.IntegerPositive:
+ case CbFieldType.IntegerNegative:
+ return VarInt.measure(this._data_view);
+ case CbFieldType.Float32:
+ return 4;
+ case CbFieldType.Float64:
+ return 8;
+ case CbFieldType.BoolFalse:
+ case CbFieldType.BoolTrue:
+ return 0;
+ case CbFieldType.ObjectAttachment:
+ case CbFieldType.BinaryAttachment:
+ case CbFieldType.Hash:
+ return 20;
+ case CbFieldType.Uuid:
+ return 16;
+ case CbFieldType.ObjectId:
+ return 12;
+ case CbFieldType.DateTime:
+ case CbFieldType.TimeSpan:
+ return 8;
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype._is = function(func) { return func(this.get_type()); }
+CbFieldView.prototype.is_null = function() { return this._is(CbFieldTypeOps.is_null); }
+CbFieldView.prototype.is_object = function() { return this._is(CbFieldTypeOps.is_object); }
+CbFieldView.prototype.is_array = function() { return this._is(CbFieldTypeOps.is_array); }
+CbFieldView.prototype.is_binary = function() { return this._is(CbFieldTypeOps.is_binary); }
+CbFieldView.prototype.is_string = function() { return this._is(CbFieldTypeOps.is_string); }
+CbFieldView.prototype.is_integer = function() { return this._is(CbFieldTypeOps.is_integer); }
+CbFieldView.prototype.is_float = function() { return this._is(CbFieldTypeOps.is_float); }
+CbFieldView.prototype.is_bool = function() { return this._is(CbFieldTypeOps.is_bool); }
+CbFieldView.prototype.is_object_attachment = function() { return this._is(CbFieldTypeOps.is_object_attachment); }
+CbFieldView.prototype.is_binary_attachment = function() { return this._is(CbFieldTypeOps.is_binary_attachment); }
+CbFieldView.prototype.is_attachment = function() { return this._is(CbFieldTypeOps.is_attachment); }
+CbFieldView.prototype.is_hash = function() { return this._is(CbFieldTypeOps.is_hash); }
+CbFieldView.prototype.is_uuid = function() { return this._is(CbFieldTypeOps.is_uuid); }
+CbFieldView.prototype.is_object_id = function() { return this._is(CbFieldTypeOps.is_object_id); }
+CbFieldView.prototype.is_date_time = function() { return this._is(CbFieldTypeOps.is_date_time); }
+CbFieldView.prototype.is_time_span = function() { return this._is(CbFieldTypeOps.is_time_span); }
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.as_object = function()
+{
+ cb_assert(CbFieldTypeOps.is_object(this.get_type()));
+ return new CbObjectView()._from_field(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.as_array = function()
+{
+ cb_assert(CbFieldTypeOps.is_array(this.get_type()));
+ return new CbArrayView()._from_field(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbFieldView.prototype.as_value = function(int_type=Number)
+{
+ switch (CbFieldTypeOps.get_type(this.get_type()))
+ {
+ case CbFieldType.None: return undefined;
+ case CbFieldType.Null: return null;
+
+ case CbFieldType.Object:
+ case CbFieldType.UniformObject: return this.as_object();
+
+ case CbFieldType.Array:
+ case CbFieldType.UniformArray: return this.as_array();
+
+ case CbFieldType.Binary: {
+ const [n, vn] = VarInt.read_uint(this._data_view);
+ return this._data_view.subarray(vn, n + vn);
+ }
+
+ case CbFieldType.String: {
+ const [n, vn] = VarInt.read_uint(this._data_view);
+ return new TextDecoder().decode(this._data_view.subarray(vn, n + vn));
+ }
+
+ case CbFieldType.IntegerPositive: return VarInt.read_uint(this._data_view, int_type)[0];
+ case CbFieldType.IntegerNegative: return VarInt.read_int(this._data_view, int_type)[0];
+
+ case CbFieldType.Float32: return new DataView(this._data_view.subarray(0, 4)).getFloat32(0, false);
+ case CbFieldType.Float64: return new DataView(this._data_view.subarray(0, 8)).getFloat64(0, false);
+ case CbFieldType.BoolFalse: return false;
+ case CbFieldType.BoolTrue: return true;
+
+ case CbFieldType.ObjectAttachment:
+ case CbFieldType.BinaryAttachment:
+ case CbFieldType.Hash: return this._data_view.subarray(0, 20);
+
+ case CbFieldType.Uuid: return this._data_view.subarray(0, 16);
+ case CbFieldType.ObjectId: return this._data_view.subarray(0, 12);
+
+ case CbFieldType.DateTime:
+ case CbFieldType.TimeSpan: return this._data_view.subarray(0, 8);
+ }
+
+ cb_assert(false);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class CbObjectView extends CbFieldView
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbObjectView.prototype[Symbol.iterator] = function()
+{
+ var data_view = this.get_payload();
+
+ const [payload_size, varint_len] = VarInt.read_uint(data_view);
+ if (payload_size == 0)
+ return {};
+ data_view = data_view.subarray(varint_len, payload_size + varint_len);
+
+ var uniform_type = CbFieldType.HasFieldType;
+ if (CbFieldTypeOps.get_type(this.get_type()) == CbFieldType.UniformObject)
+ {
+ uniform_type = data_view[0];
+ data_view = data_view.subarray(1);
+ }
+
+ return CbFieldView._iterate(data_view, uniform_type);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbObjectView.prototype.to_js_object = function()
+{
+ const impl = function(node)
+ {
+ if (node.is_object())
+ {
+ const ret = {};
+ for (var item of node.as_object())
+ ret[item.get_name()] = impl(item);
+ return ret;
+ }
+
+ if (node.is_array())
+ {
+ const ret = [];
+ for (var item of node.as_array())
+ ret.push(impl(item));
+ return ret;
+ }
+
+ if (node.is_string()) return node.as_value();
+ if (node.is_float()) return node.as_value();
+ if (node.is_integer()) return node.as_value();
+
+ var ret = node.as_value();
+ if (ret instanceof Uint8Array)
+ {
+ ret = "";
+ for (var x of node.as_value())
+ ret += x.toString(16).padStart(2, "0");
+ }
+ return ret;
+ };
+
+ return impl(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbObjectView.prototype.find = function(name)
+{
+ for (const field of this)
+ if (field.get_name() == name)
+ return field;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class CbArrayView extends CbFieldView
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbArrayView.prototype[Symbol.iterator] = function()
+{
+ var data_view = this.get_payload();
+
+ const [payload_size, varint_len] = VarInt.read_uint(data_view);
+ data_view = data_view.subarray(varint_len, payload_size + varint_len);
+
+ const item_count_bytes = VarInt.measure(data_view);
+ if (item_count_bytes >= payload_size)
+ return {};
+ data_view = data_view.subarray(item_count_bytes);
+
+ var uniform_type = CbFieldType.HasFieldType;
+ if (CbFieldTypeOps.get_type(this.get_type()) == CbFieldType.UniformArray)
+ {
+ uniform_type = data_view[0];
+ data_view = data_view.subarray(1);
+ }
+
+ return CbFieldView._iterate(data_view, uniform_type);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+CbArrayView.prototype.num = function()
+{
+ var data_view = this._data_view;
+ const [n, n_len] = VarInt.read_uint(data_view);
+ data_view = data_view.subarray(n_len);
+ return VarInt.read_uint(data_view)[0];
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class CbObject extends CbFieldView
+{
+ constructor(uint8_array)
+ {
+ super();
+ this._from_data(uint8_array);
+ }
+}