@d3x0r/sack-gui
Version:
SACK abstraction library exposed to JS to provide low level system services.
1,027 lines (927 loc) • 34.9 kB
JavaScript
class DateNS extends Date {
ns=0;
constructor(a,b ) {
if( !a ) super();
else super(a);
this.ns = b || 0;
}
toString() {
return toISONS( this );
}
toISOString() {
return toISONS( this );
}
}
function toISO(this_) {
if( this_.getTime()=== -62167219200000)
{
return "0000-01-01T00:00:00.000Z";
}
const yr = this_.getFullYear();
const tzo = -this_.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 10 ? '0' : '') + norm;
},
pad3 = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 100 ? '0' : '') + (norm < 10 ? '0' : '') + norm;
};
return (( yr < 0 )? '-'+Math.abs(yr).toString().padStart( 6, '0' ):yr.toString().padStart( 4, "0" )) +
'-' + pad(this_.getMonth() + 1) +
'-' + pad(this_.getDate()) +
'T' + pad(this_.getHours()) +
':' + pad(this_.getMinutes()) +
':' + pad(this_.getSeconds()) +
'.' + pad3(this_.getMilliseconds()) +
dif + pad(tzo / 60) +
':' + pad(tzo % 60.0);
}
function toISONS(this_) {
var tzo = -this_.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 10 ? '0' : '') + norm;
},
pad3 = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 100 ? '0' : '') + (norm < 10 ? '0' : '') + norm;
},
pad6 = function(num) {
var norm = Math.floor(Math.abs(num));
return (norm < 100000 ? '0' : '') + (norm < 10000 ? '0' : '') + (norm < 1000 ? '0' : '') + (norm < 100 ? '0' : '') + (norm < 10 ? '0' : '') + norm;
};
return this_.getFullYear() +
'-' + pad(this_.getMonth() + 1) +
'-' + pad(this_.getDate()) +
'T' + pad(this_.getHours()) +
':' + pad(this_.getMinutes()) +
':' + pad(this_.getSeconds()) +
'.' + pad3(this_.getMilliseconds()) + pad6(this_.ns) +
dif + pad(tzo / 60) +
':' + pad(tzo % 60);
}
export default function(sack) {
sack.JSON6.stringify = JSON.stringify;
sack.JSON.stringify = JSON.stringify;
try {
var disk = sack.Volume();
require.extensions['.json6'] = function (module, filename) {
var content = disk.read(filename).toString();
module.exports = sack.JSON6.parse(content);
};
require.extensions['.jsox'] = function (module, filename) {
var content = disk.read(filename).toString();
module.exports = sack.JSOX.parse(content);
};
} catch(err) {
console.log( "JSOX Module could not register require support...", err.message );
}
const _DEBUG_STRINGIFY_TIMING = false;
const _DEBUG_STRINGIFY = false;
const DEBUG_STRINGIFY_OUTPUT = _DEBUG_STRINGIFY|| false;
let toProtoTypesByName = new Map();
let toProtoTypes = new WeakMap();
let toProtoTypeRegistrations = []; // external registrations may need to be updated too....
let toObjectTypes = new Map();
let fromProtoTypes = new Map();
let commonClasses = [];
sack.JSOX.fromProtoType = fromProtoTypes;
sack.JSOX.reset = initPrototypes;
function pushToProto(p,a) {
if( !toProtoTypesByName.get( p.constructor.name ) )
toProtoTypesByName.set( p.constructor.name, a );
else
console.log( "duplicating...", p );
toProtoTypes.set( p, a );
}
function escape(string) {
//return string.replace( "\\", "\\\\" ).replace( '\"', "\\\"" ).replace( "\'", "\\\'" );
let n;
let output = '';
if( !string ) return string;
//console.log( "escape:", string );
for( n = 0; n < string.length; n++ ) {
if( ( string[n] == '"' ) || ( string[n] == '\\' ) || ( string[n] == '`' )|| ( string[n] == '\'' )) {
output += '\\';
}
output += string[n];
}
return output;
}
sack.JSOX.DateNS = DateNS;
initPrototypes();
function initPrototypes()
{
//console.log( "Doing setup of classes:", toProtoTypes.get( Object.getPrototypeOf( [] ) ) );
// hook module native code to JS interface.
toProtoTypesByName = new Map();
toProtoTypes = new WeakMap();
toProtoTypeRegistrations = []; // external registrations may need to be updated too....
toObjectTypes = new Map();
fromProtoTypes = new Map();
commonClasses = [];
sack.JSOX.setFromPrototypeMap( fromProtoTypes, DateNS );
pushToProto( Object.prototype, { external:false, name:Object.prototype.constructor.name, cb:null } );
function this_value() {_DEBUG_STRINGIFY&&console.log( "this:", this, "valueof:", this&&this.valueOf() ); return this&&this.valueOf(); }
// function https://stackoverflow.com/a/17415677/4619267
pushToProto( Date.prototype, { external:false,
name : null, // this doesn't get a tag name, it returns a literal.
cb() { return toISO( this ) }
} );
pushToProto( DateNS.prototype, { external:false,
name : null, // this doesn't get a tag name, it returns a literal.
cb() { return toISONS(this) }
} );
pushToProto( Boolean.prototype, { external:false, name:null, cb:this_value } );
pushToProto( Number.prototype, { external:false, name:null
, cb:function(){
if( isNaN(this) ) return "NaN";
return (isFinite(this))
? String(this)
: (this<0)?"-Infinity":"Infinity";
}
} );
pushToProto( String.prototype, { external:false
, name : null
, cb:function(){ return '"' + escape(this_value.apply(this)) + '"' } } );
if( typeof BigInt === "function" )
pushToProto( BigInt.prototype
, { external:false, name:null, cb:function() { console.log( "BIGINT TOSTR"); return this + 'n' } } );
const useQuote = '"';
function getIdentifier(s) {
if( ( "string" === typeof s ) && s === '' ) return useQuote+useQuote;
if( ( "number" === typeof s ) && !isNaN( s ) ) {
return ["'",s.toString(),"'"].join('');
}
// should check also for if any non ident in string...
if( s.includes( "\u{FEFF}" ) ) return (useQuote + escape(s) +useQuote);
return ( ( s in keywords /* [ "true","false","null","NaN","Infinity","undefined"].find( keyword=>keyword===s )*/
|| /([0-9-])/.test(s[0])
|| /((\n|\r|\t)|[ #\[\]{}()<>!+*/.:,-])/.test( s ) )?(useQuote + escape(s) +useQuote):s )
}
pushToProto( ArrayBuffer.prototype, { external:true, name:"ab"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this))+"]" }
} );
pushToProto( Uint8Array.prototype, { external:true, name:"u8"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Uint8ClampedArray.prototype, { external:true, name:"uc8"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Int8Array.prototype, { external:true, name:"s8"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Uint16Array.prototype, { external:true, name:"u16"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Int16Array.prototype, { external:true, name:"s16"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Uint32Array.prototype, { external:true, name:"u32"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Int32Array.prototype, { external:true, name:"s32"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
if( typeof Uint64Array !== "undefined" )
pushToProto( Uint64Array.prototype, { external:true, name:"u64"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
if( typeof Int64Array !== "undefined" )
pushToProto( Int64Array.prototype, { external:true, name:"s64"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Float32Array.prototype, { external:true, name:"f32"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Float64Array.prototype, { external:true, name:"f64"
, cb:function() { return "["+getIdentifier(base64ArrayBuffer(this.buffer))+"]" }
} );
pushToProto( Symbol.prototype, { external:true, name:"sym"
, cb:function() { return '"'+this.description+'"' }
} );
pushToProto( RegExp.prototype, { external:true, name:"regex"
, cb:function(o,stringifier){
return "'"+escape(this.source)+"'";
}
} );
fromProtoTypes.set( "regex", { protoCon:RegExp, cb:function (field,val){
return new RegExp( this );
} } );
pushToProto( Map.prototype, mapToJSOX = { external:true, name:"map"
, cb:null
} );
fromProtoTypes.set("map", {
protoCon: Map.prototype.constructor, cb:function(field, val) {
if (!field) return this;
this.set( field,val );
} } );
pushToProto( Array.prototype, arrayToJSOX = { external:false, name:Array.prototype.constructor.name
, cb: null
} );
}
sack.JSOX.defineClass = function( name, obj ) {
var cls;
var denormKeys = Object.keys(obj);
for( var i = 1; i < denormKeys.length; i++ ) {
var a, b;
if( ( a = denormKeys[i-1] ) > ( b = denormKeys[i] ) ) {
denormKeys[i-1] = b;
denormKeys[i] = a;
if( i ) i-=2; // go back 2, this might need to go further pack.
else i--; // only 1 to check.
}
}
//console.log( "normalized:", denormKeys );
commonClasses.push( cls = { name : name
, tag:denormKeys.toString()
, proto : Object.getPrototypeOf(obj)
, fields : Object.keys(obj) } );
for(var n = 1; n < cls.fields.length; n++) {
if( cls.fields[n] < cls.fields[n-1] ) {
let tmp = cls.fields[n-1];
cls.fields[n-1] = cls.fields[n];
cls.fields[n] = tmp;
if( n > 1 )
n-=2;
}
}
if( cls.proto === Object.getPrototypeOf( {} ) ) cls.proto = null;
}
sack.JSOX.registerToJSOX = function( name, prototype, f ) {
_DEBUG_STRINGIFY &&
console.log( "Register prototype:", prototype, prototype.prototype );
if( !prototype.prototype || prototype.prototype !== Object.prototype ) {
if( toProtoTypes.get(prototype.prototype) ) {
console.trace( "Duplicated definition of toJSOX for", name );
//throw new Error( "Existing toJSOX has been registered for prototype" );
}else {
_DEBUG_STRINGIFY && console.log( "PUSH PROTOTYPE" );
pushToProto( prototype.prototype, { external:true, name:(name===undefined)?f.prototype.constructor.name:name, cb:f } );
}
} else {
var key = Object.keys( prototype ).toString();
if( toObjectTypes.get(key) ) throw new Error( "Existing toJSOX has been registered for object type" );
_DEBUG_STRINGIFY && console.log( "TEST SET OBJECT TYPE:", key );
toObjectTypes.set( key, { external:true, name:name, cb:f } );
}
}
sack.JSOX.updateContext = function() {
if( toProtoTypes.get( Map.prototype ) ) return;
console.log( "Do init protoypes for new context objects..." );
initPrototypes();
}
sack.JSOX.toJSOX = sack.JSOX.registerToJSOX;
sack.JSOX.registerFromJSOX = function (prototypeName, o, f) {
throw new Error("registerFromJSOX was deprecated, please update to use 'fromJSOX'");
}
sack.JSOX.fromJSOX = function( prototypeName, o, f ) {
//console.log( "Registration:", prototypeName, o );
if( fromProtoTypes.get(prototypeName) ) throw new Error( "Existing fromJSOX has been registered for prototype" );
if( o && !("constructor" in o )) {
throw new Error( "Please pass a proper prototype...." );
}
var z;
fromProtoTypes.set(prototypeName, z = { protoCon: o && o.prototype.constructor, cb: f });
//console.log( "registered", z );
}
sack.JSOX.registerToFrom = function( prototypeName, prototype, to, from ) {
//console.log( "INPUT:", prototype );
sack.JSOX.registerToJSOX( prototypeName, prototype, to );
sack.JSOX.fromJSOX( prototypeName, prototype, from );
}
sack.JSOX.addType = function( prototypeName, prototype, to, from ) {
sack.JSOX.toJSOX( prototypeName, prototype, to );
sack.JSOX.fromJSOX( prototypeName, prototype, from );
}
var JSOXBegin = sack.JSOX.begin;
sack.JSOX.begin = function(cb, reviver) {
var parser = JSOXBegin( cb, reviver );
var localFromProtoTypes = new Map();;
var localPromiseFromProtoTypes = new Map();;
parser.localFromProtoTypes = localFromProtoTypes;
parser.setFromPrototypeMap( localFromProtoTypes );
parser.setPromiseFromPrototypeMap( localPromiseFromProtoTypes );
parser.registerFromJSOX = function (prototypeName, o, f) {
throw new Error("registerFromJSOX was deprecated, please update to use 'fromJSOX'");
}
parser.fromJSOX = function( prototypeName, o, f ) {
const existed = !! localFromProtoTypes.get(prototypeName);
if(o && !("constructor" in o))
throw new Error("Please pass a prototype like object...");
localFromProtoTypes.set( prototypeName, { protoCon:o && o.prototype.constructor, cb:f } );
return existed;
}
parser.registerPromiseFromJSOX = function( prototypeName, o, f ) {
throw new Error( "Deprecated 'registerPromiseFromJSOX', pluse use fromJSOX has been registered for prototype" );
}
return parser;
}
var arrayToJSOX;
var mapToJSOX;
const keywords = { ["true"]:true,["false"]:false,["null"]:null,["NaN"]:NaN,["Infinity"]:Infinity,["undefined"]:undefined }
var id = 1;
sack.JSOX.stringifierActive = null;
let timeIn = 0;
let timeOut = 0;
let lastTick = Date.now();
let lastLog = Date.now();
sack.JSOX.stringifier = function() {
var classes = [];
var useQuote = '"';
var fieldMap = new WeakMap();
var path = [];
var encoding = [];
var objectToJSOX = null;
var localToProtoTypes = new WeakMap();
localToProtoTypes.id = id++;
var localToObjectTypes = new Map();
const stringifying = []; // things that have been stringified through external toJSOX; allows second pass to skip this toJSOX pass and encode 'normally'
var ignoreNonEnumerable = false;
return {
defineClass(name,obj) {
var cls;
var denormKeys = Object.keys(obj);
for( var i = 1; i < denormKeys.length; i++ ) {
var a, b;
if( ( a = denormKeys[i-1] ) > ( b = denormKeys[i] ) ) {
denormKeys[i-1] = b;
denormKeys[i] = a;
if( i ) i-=2; // go back 2, this might need to go further pack.
else i--; // only 1 to check.
}
}
classes.push( cls = { name : name
, tag:denormKeys.toString()
, proto : Object.getPrototypeOf(obj)
, fields : Object.keys(obj) } );
for(var n = 1; n < cls.fields.length; n++) {
if( cls.fields[n] < cls.fields[n-1] ) {
let tmp = cls.fields[n-1];
cls.fields[n-1] = cls.fields[n];
cls.fields[n] = tmp;
if( n > 1 )
n-=2;
}
}
if( cls.proto === Object.getPrototypeOf( {} ) ) cls.proto = null;
},
setDefaultObjectToJSOX( cb ) { objectToJSOX = cb },
isEncoding(o) {
//console.log( "is object encoding?", encoding.length, o, encoding );
return !!encoding.find( (eo,i)=>eo===o && i < (encoding.length-1) )
},
encodeObject(o) {
if( objectToJSOX )
return objectToJSOX.apply(o, [this]);
return o;
},
stringify(o,r,s,as) { return stringify(this, o,r,s,as) },
setQuote(q) { useQuote = q; },
toJSOX( name,proto,f) { return this.registerToJSOX( name,proto,f) },
registerToJSOX( name, ptype, f ) {
if( ptype.prototype && ptype.prototype !== Object.prototype ) {
if( localToProtoTypes.get(ptype) ) throw new Error( "Existing toJSOX has been registered for prototype" );
_DEBUG_STRINGIFY && console.log( "Adding prototype to local objects:", name, ptype.prototype, localToProtoTypes );
const newThing = { external:true, name:(name===undefined)?f.prototype.constructor.name:name, cb:f };
localToProtoTypes.set( ptype.prototype, newThing );
} else {
_DEBUG_STRINGIFY && console.log( "This is set by key?!" );
var key = Object.keys( ptype ).toString();
if( localToObjectTypes.get(key) ) throw new Error( "Existing toJSOX has been registered for object type" );
localToObjectTypes.set( key, { external:true, name:name, cb:f } );
}
},
getReference: getReference,
get ignoreNonEnumerable() { return ignoreNonEnumerable; },
set ignoreNonEnumerable(val) { ignoreNonEnumerable = val; },
}
function getReference( here ) {
if( here === null ) return undefined;
var field = fieldMap.get( here );
_DEBUG_STRINGIFY && console.trace( "get reference path:", here, sack.JSON.stringify(path), field );
if( !field ) {
fieldMap.set( here, sack.JSON.stringify(path) );
return undefined;
}
return 'ref'+field;
}
function getIdentifier(s) {
if( ( "string" === typeof s ) && s === '' ) return useQuote+useQuote;
if( ( "number" === typeof s ) && !isNaN( s ) ) {
return ["'",s.toString(),"'"].join('');
}
// should check also for if any non ident in string...
if( s.includes( "\u{FEFF}" ) ) return (useQuote + escape(s) +useQuote);
return ( ( s in keywords /* [ "true","false","null","NaN","Infinity","undefined"].find( keyword=>keyword===s )*/
|| /([0-9-])/.test(s[0])
|| /((\n|\r|\t)|[ #\[\]{}()<>!+*/.:,-])/.test( s ) )?(useQuote + escape(s) +useQuote):s )
}
function matchObject(o,useK) {
var k;
var cls;
var prt = Object.getPrototypeOf(o);
cls = classes.find( cls=>{
if( cls.proto && cls.proto === prt ) return true;
} );
if( cls ) return cls;
cls = commonClasses.find( cls=>{
if( cls.proto && cls.proto === prt ) return true;
} );
if( cls ) return cls;
if( classes.length || commonClasses.length ) {
if( useK ) {
useK = useK.map( v=>{ if( typeof v === "string" ) return v; else return undefined; } );
k = useK.toString();
} else {
var denormKeys = Object.keys(o);
for( var i = 1; i < denormKeys.length; i++ ) {
var a, b;
if( ( a = denormKeys[i-1] ) > ( b = denormKeys[i] ) ) {
denormKeys[i-1] = b;
denormKeys[i] = a;
if( i ) i-=2; // go back 2, this might need to go further pack.
else i--; // only 1 to check.
}
}
k = denormKeys.toString();
}
cls = classes.find( cls=>{
if( cls.tag === k ) return true;
} );
if( !cls )
cls = commonClasses.find( cls=>{
if( cls.tag === k ) return true;
} );
}
return cls;
}
function stringify( stringifier, object, replacer, space, asField ) {
if( object === undefined ) return "undefined";
if( object === null ) return "null";
var gap;
var indent;
var rep;
if( _DEBUG_STRINGIFY_TIMING ) {
let now = Date.now();
timeOut += now - lastTick;
lastTick = now;
}
var i;
const spaceType = typeof space;
const repType = typeof replacer;
gap = "";
indent = "";
const stringifier_ = sack.JSOX.stringifierActive;
sack.JSOX.stringifierActive = stringifier;
const pathBase = path.length;
if( !asField ) {
asField = "";
}else {
path.push( asField );
encoding[pathBase] = object;
}
if( "object" === typeof object && stringifier_ ) {
var ref = stringifier_.getReference( object );
//console.log("This added object as ref", ref, object );
if( ref ) {
if( !(path.length = encoding.length = pathBase ) ) fieldMap = new WeakMap();
return ref;
}else { //if( asField )
fieldMap.delete(object)
//console.log( "Deleting object reference from fieldMap" );
}
}
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (spaceType === "number") {
for (i = 0; i < space; i += 1) {
indent += " ";
}
// If the space parameter is a string, it will be used as the indent string.
} else if (spaceType === "string") {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if( replacer && repType !== "function"
&& ( repType !== "object"
|| typeof replacer.length !== "number"
)) {
throw new Error("JSOX.stringify");
}
const r = str( asField, {[asField]:object} );
sack.JSOX.stringifierActive = stringifier_;
commonClasses.length = 0;
if( !(path.length = encoding.length = pathBase ) ){
fieldMap = new WeakMap();
}else{
//console.log( "Stringifier is still in a stack?", path);
}
DEBUG_STRINGIFY_OUTPUT && console.log( "Stringify Result:", object, "=",r );
if(_DEBUG_STRINGIFY_TIMING) {
let now = Date.now();
timeIn += now - lastTick;
lastTick = now;
if( now - lastLog > 250 ) {
console.log( "stringify profile:", timeIn, timeOut );
lastLog = now;
}
}
return r;
function str(key, holder) {
var mind = gap;
const doArrayToJSOX_ = arrayToJSOX.cb;
const mapToObject_ = mapToJSOX.cb;
arrayToJSOX.cb = doArrayToJSOX;
mapToJSOX.cb = mapToObject;
const v = str_(key,holder);
arrayToJSOX.cb = doArrayToJSOX_;
mapToJSOX.cb = mapToObject_;
return v;
function doArrayToJSOX() {
var v;
var partial = [];
const thisNodeNameIndex = path.length;
{
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSOX values.
for (let i = 0; i < this.length; i += 1) {
path[thisNodeNameIndex] = i;
partial[i] = str(i, this) || "null";
}
path.length = thisNodeNameIndex;
//console.log( "remove encoding item", thisNodeNameIndex, encoding.length);
encoding.length = thisNodeNameIndex;
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = ( partial.length === 0
? "[]"
: gap
? [
"[\n"
, gap
, partial.join(",\n" + gap)
, "\n"
, mind
, "]"
].join("")
: "[" + partial.join(",") + "]" );
return v;
}
}
function mapToObject(){
var tmp = {tmp:null};
var out = '{'
var first = true;
//console.log( "CONVERT:", map);
for (var [key, value] of this) {
if( key === undefined ) {
console.log( "Found a key 'undefined' in this?", this );
continue;
}
//if( "function" === typeof value ) continue;
//console.log( "er...", key, value )
tmp.tmp = value;
const thisNodeNameIndex = path.length;
path[thisNodeNameIndex] = key;
out += (first?"":",") + getIdentifier(key) +':' + str("tmp", tmp);
path.length = thisNodeNameIndex;
first = false;
}
out += '}';
return out;
}
// from https://github.com/douglascrockford/JSON-js/blob/master/json2.js#L181
// (highly modified since then)
function str_(key, holder) {
//console.trace( "-------------------------------------- STR -----------------------", key, holder );
//console.log( "Encode object:", holder[key], "field:", key );
// Produce a string from holder[key].
var i; // The loop counter.
var k; // The member key.
var v; // The member value.
var length;
var partialClass;
var partial;
const thisNodeNameIndex = path.length;
var value = holder[key];
let isObject = (typeof value === "object");
if( "string" === typeof value ) value = getIdentifier( value );
_DEBUG_STRINGIFY &&
console.log( "Prototype lists:", localToProtoTypes.length, value && localToProtoTypes.get( Object.getPrototypeOf( value ) )
, value && Object.getPrototypeOf( value ), value && value.constructor.name
);
if( isObject && ( value !== null ) ) {
if( objectToJSOX ){
_DEBUG_STRINGIFY &&
console.log( "doing generic object conversion phase..." );
if( !stringifying.find( val=>val===value ) ) {
stringifying.push( value );
encoding[thisNodeNameIndex] = value;
value = objectToJSOX.apply(value, [stringifier]);
_DEBUG_STRINGIFY &&
console.log( "Value should still be value:", value );
/*
if( value !== encoding[thisNodeNameIndex] )
console.log( "Converted by object lookup -it's now a different type"
, Object.getPrototypeOf(encoding[thisNodeNameIndex])
, Object.getPrototypeOf(value )
, protoConverter, objectConverter );
*/
stringifying.pop();
encoding.length = thisNodeNameIndex;
isObject = (typeof value === "object");
}
}
}
var protoConverter = (value !== undefined && value !== null)
&& ( localToProtoTypes.get( Object.getPrototypeOf( value ) )
|| toProtoTypes.get( Object.getPrototypeOf( value ) )
|| ( Object.getPrototypeOf(value) && toProtoTypesByName.get( Object.getPrototypeOf( value ).constructor.name ))
|| null )
var objectConverter = !protoConverter && (value !== undefined && value !== null)
&& ( localToObjectTypes.get( Object.keys( value ).toString() )
|| toObjectTypes.get( Object.keys( value ).toString() )
|| null )
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === "function") {
value = rep.call(holder, key, value);
}
var toJSOX = ( protoConverter && protoConverter.cb )
|| ( objectConverter && objectConverter.cb )
//|| ( isObject && objectToJSOX )
;
// If the value has a toJSOX method, call it to obtain a replacement value.
_DEBUG_STRINGIFY && console.log( "type:", typeof value, protoConverter, !!toJSOX, path, isObject );
if( value !== undefined
&& value !== null
&& typeof value === "object"
&& typeof toJSOX === "function"
) {
// is encoding?
if( !stringifying.find( val=>val===value ) ) {
if( typeof value === "object" ) {
v = getReference( value );
if( v ) return v;
}
stringifying.push( value );
encoding[thisNodeNameIndex] = value;
value = toJSOX.call(value, stringifier);
stringifying.pop();
if( protoConverter && protoConverter.name ) {
// stringify may return a unquoted string
// which needs an extra space betwen its tag and value.
if( "string" === typeof value
&& value[0] !== '"'
&& value[0] !== '\''
&& value[0] !== '`'
&& value[0] !== '['
&& value[0] !== '{'
){
value = ' ' + value;
}
}
//console.log( "Value converted:", value );
encoding.length = thisNodeNameIndex;
} else {
v = getReference( value );
}
} else
if( typeof value === "object" ) {
v = getReference( value );
}
_DEBUG_STRINGIFY && console.log( "Protoconverter leftover: ", protoConverter );
// What happens next depends on the value's type.
switch (typeof value) {
case "bigint":
return value + 'n';
case "string":
{
let c = '';
//console.log( "outputting string result:", value, c );
if( key==="" ) {
c = classes.map( cls=> cls.name+"{"+cls.fields.join(",")+"}" ).join(gap?"\n":"")+(gap?"\n":"")
|| commonClasses.map( cls=> cls.name+"{"+cls.fields.join(",")+"}" ).join(gap?"\n":"")+(gap?"\n":"");
}
if( protoConverter && protoConverter.external ){
// && protoConverter.proto === Object.getPrototypeOf(value) && protoConverter.name
return c + protoConverter.name + value;
}
if( objectConverter && objectConverter.external )
return c + objectConverter.name + value;
return c + value;//useQuote+JSOX.escape( value )+useQuote;
}
case "number":
case "boolean":
//case "null":
// If the value is a number, boolean or null, convert it to a string. Note:
// typeof null does not produce "null". The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is "object", we might be dealing with an object or an array or
// null.
case "object":
_DEBUG_STRINGIFY && console.log( "ENTERING OBJECT EMISSION WITH:", v, value );
if( v ) return v;
// Due to a specification blunder in ECMAScript, typeof null is "object",
// so watch out for that case.
if (!value) {
return "null";
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partialClass = null;
partial = [];
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === "object") {
length = rep.length;
_DEBUG_STRINGIFY && console.log( "Working through replacer" );
partialClass = matchObject( value, rep );
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === "string") {
k = rep[i];
path[thisNodeNameIndex] = k;
//console.log( "set encoding item", thisNodeNameIndex, encoding.length);
encoding[thisNodeNameIndex] = value;
v = str(k, value);
if (v) {
if( partialClass ) {
partial.push(v);
} else
partial.push(getIdentifier(k) + (
(gap)
? ": "
: ":"
) + v);
}
}
}
path.length = thisNodeNameIndex;
//console.log( "remove encoding item", thisNodeNameIndex, encoding.length);
encoding.length = thisNodeNameIndex;
} else {
// Otherwise, iterate through all of the keys in the object.
partialClass = matchObject( value );
var keys = [];
_DEBUG_STRINGIFY && console.log( "is something in something?", k, value );
for (k in value) {
_DEBUG_STRINGIFY && console.log( "Ya...", k );
if( ignoreNonEnumerable )
if( !Object.prototype.propertyIsEnumerable.call( value, k ) ){
_DEBUG_STRINGIFY && console.log( "skipping non-enuerable?", k );
continue;
}
// sort properties into keys.
if (Object.prototype.hasOwnProperty.call(value, k)) {
var n;
for( n = 0; n < keys.length; n++ )
if( keys[n] > k ) {
keys.splice(n,0,k );
break;
}
if( n === keys.length )
keys.push(k);
}
}
_DEBUG_STRINGIFY && console.log( "Expanding object keys:", v, keys );
for(let k of keys ) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
path[thisNodeNameIndex] = k;
encoding[thisNodeNameIndex] = value;
v = str(k, value);
if (v) {
if( partialClass ) {
partial.push(v);
} else
partial.push(getIdentifier(k)+ (
(gap)
? ": "
: ":"
) + v);
}
}
}
path.length = thisNodeNameIndex;
//console.log( "remove encoding item", thisNodeNameIndex, encoding.length);
encoding.length = thisNodeNameIndex;
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
_DEBUG_STRINGIFY && console.log( "partial:", partial, protoConverter )
{
let c;
if( key==="" )
c = classes.map( cls=> cls.name+"{"+cls.fields.join(",")+"}" ).join(gap?"\n":"")+(gap?"\n":"")
|| commonClasses.map( cls=> cls.name+"{"+cls.fields.join(",")+"}" ).join(gap?"\n":"")+(gap?"\n":"");
else
c = '';
if( protoConverter && protoConverter.external ) {
c = c + getIdentifier( protoConverter.name );
}
var ident = null;
if( partialClass )
ident = getIdentifier( partialClass.name );
v = c +
( partial.length === 0
? "{}"
: gap
? (partialClass?ident:"")+"{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
: (partialClass?ident:"")+"{" + partial.join(",") + "}"
);
}
gap = mind;
_DEBUG_STRINGIFY && console.log(" Resulting phrase from this part is:", v );
return v;
}
}
}
}
}
// Converts an ArrayBuffer directly to base64, without any intermediate 'convert to string then
// use window.btoa' step. According to my tests, this appears to be a faster approach:
// http://jsperf.com/encoding-xhr-image-data/5
// doesn't have to be reversable....
const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_'
const decodings = { '=':-1, '+':62, '/':63, '.':62, ',':63 };
for( var x = 0; x < 256; x++ ) {
if( x < 64 ) {
decodings[encodings[x]] = x;
}
}
function base64ArrayBuffer(arrayBuffer) {
var base64 = ''
var bytes = new Uint8Array(arrayBuffer)
var byteLength = bytes.byteLength
var byteRemainder = byteLength % 3
var mainLength = byteLength - byteRemainder
var a, b, c, d
var chunk
//throw "who's using this?"
//console.log( "buffer..", arrayBuffer )
// Main loop deals with bytes in chunks of 3
for (var i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
d = chunk & 63 // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
}
// Deal with the remaining bytes and padding
if (byteRemainder == 1) {
chunk = bytes[mainLength]
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4 // 3 = 2^2 - 1
base64 += encodings[a] + encodings[b] + '=='
} else if (byteRemainder === 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2 // 15 = 2^4 - 1
base64 += encodings[a] + encodings[b] + encodings[c] + '='
}
//console.log( "dup?", base64)
return base64
}
function DecodeBase64( buf )
{
//console.log( "length:", buf.length, (((buf.length+3)/4)|0), (buf[buf.length-1]==='='?1:0), (buf[buf.length-2]==='='?1:0) )
var ab = new ArrayBuffer( (3*(((buf.length+3)>>2)|0)) - ((buf[buf.length-1]==='='?1:0) + (buf[buf.length-2]==='='?1:0)) );
//console.log( "LENGHT:", (3*(((buf.length+3)/4)|0)) - ((buf[buf.length-1]==='='?1:0) + (buf[buf.length-2]==='='?1:0)) );
var out = new Uint8Array(ab);
{
var n;
var l = (buf.length+3)>>2;
for( n = 0; n < l; n++ )
{
var index0 = decodings[buf[n*4]];
var index1 = decodings[buf[n*4+1]];
var index2 = decodings[buf[n*4+2]];
var index3 = decodings[buf[n*4+3]];
out[n*3+0] = (( index0 ) << 2 | ( index1 ) >> 4);
if( index2 >= 0 )
out[n*3+1] = (( index1 ) << 4 | ( ( ( index2 ) >> 2 ) & 0x0f ));
if( index3 >= 0 )
out[n*3+2] = (( index2 ) << 6 | ( ( index3 ) & 0x3F ));
}
}
return ab;
}
sack.JSOX.stringify = function( object, replacer, space ) {
var stringifier = sack.JSOX.stringifier();
return stringifier.stringify( object, replacer, space );
}
const nonIdent =
[ [ 0,264,[ 0xffd9ff,0xff6aff,0x1fc00,0x380000,0x0,0xfffff8,0xffffff,0x7fffff,0x800000,0x0,0x80 ] ] ].map( row=>{ return{ firstChar : row[0], lastChar: row[1], bits : row[2] }; } );
}