jsroot
Version:
JavaScript ROOT
1,374 lines (1,197 loc) • 140 kB
JavaScript
import { createHttpRequest, BIT, internals, settings, browser,
create, getMethods, addMethods, isNodeJs, isObject, isFunc, isStr,
clTObject, clTNamed, clTString, clTObjString, clTKey, clTFile, clTList, clTMap, clTObjArray, clTClonesArray,
clTAttLine, clTAttFill, clTAttMarker, clTStyle, clTImagePalette,
clTPad, clTCanvas, clTAttCanvas, clTPolyMarker3D, clTF1, clTF12, clTF2 } from './core.mjs';
const clTStreamerElement = 'TStreamerElement', clTStreamerObject = 'TStreamerObject',
clTStreamerSTL = 'TStreamerSTL', clTStreamerInfoList = 'TStreamerInfoList',
clTDirectory = 'TDirectory', clTDirectoryFile = 'TDirectoryFile',
clTQObject = 'TQObject', clTBasket = 'TBasket', clTDatime = 'TDatime',
nameStreamerInfo = 'StreamerInfo',
kChar = 1, kShort = 2, kInt = 3, kLong = 4, kFloat = 5, kCounter = 6,
kCharStar = 7, kDouble = 8, kDouble32 = 9, kLegacyChar = 10,
kUChar = 11, kUShort = 12, kUInt = 13, kULong = 14, kBits = 15,
kLong64 = 16, kULong64 = 17, kBool = 18, kFloat16 = 19,
kBase = 0, kOffsetL = 20, kOffsetP = 40,
kObject = 61, kAny = 62, kObjectp = 63, kObjectP = 64, kTString = 65,
kTObject = 66, kTNamed = 67, kAnyp = 68, kAnyP = 69,
/* kAnyPnoVT: 70, */
kSTLp = 71,
/* kSkip = 100, kSkipL = 120, kSkipP = 140, kConv = 200, kConvL = 220, kConvP = 240, */
kSTL = 300, /* kSTLstring = 365, */
kStreamer = 500, kStreamLoop = 501,
kMapOffset = 2, kByteCountMask = 0x40000000, kNewClassTag = 0xFFFFFFFF, kClassMask = 0x80000000,
// constants of bits in version
kStreamedMemberWise = BIT(14),
// constants used for coding type of STL container
kNotSTL = 0, kSTLvector = 1, kSTLlist = 2, kSTLdeque = 3, kSTLmap = 4, kSTLmultimap = 5,
kSTLset = 6, kSTLmultiset = 7, kSTLbitset = 8,
// kSTLforwardlist = 9, kSTLunorderedset = 10, kSTLunorderedmultiset = 11, kSTLunorderedmap = 12,
// kSTLunorderedmultimap = 13, kSTLend = 14
kBaseClass = 'BASE',
// name of base IO types
BasicTypeNames = [kBaseClass, 'char', 'short', 'int', 'long', 'float', 'int', 'const char*', 'double', 'Double32_t',
'char', 'unsigned char', 'unsigned short', 'unsigned', 'unsigned long', 'unsigned', 'Long64_t', 'ULong64_t', 'bool', 'Float16_t'],
// names of STL containers
StlNames = ['', 'vector', 'list', 'deque', 'map', 'multimap', 'set', 'multiset', 'bitset'],
// TObject bits
kIsReferenced = BIT(4), kHasUUID = BIT(5),
/** @summary Custom streamers for root classes
* @desc map of user-streamer function like func(buf,obj)
* or alias (classname) which can be used to read that function
* or list of read functions
* @private */
CustomStreamers = {
TObject(buf, obj) {
obj.fUniqueID = buf.ntou4();
obj.fBits = buf.ntou4();
if (obj.fBits & kIsReferenced) buf.ntou2(); // skip pid
},
TNamed: [{
basename: clTObject, base: 1, func(buf, obj) {
if (!obj._typename) obj._typename = clTNamed;
buf.classStreamer(obj, clTObject);
}
},
{ name: 'fName', func(buf, obj) { obj.fName = buf.readTString(); } },
{ name: 'fTitle', func(buf, obj) { obj.fTitle = buf.readTString(); } }
],
TObjString: [{
basename: clTObject, base: 1, func(buf, obj) {
if (!obj._typename) obj._typename = clTObjString;
buf.classStreamer(obj, clTObject);
}
},
{ name: 'fString', func(buf, obj) { obj.fString = buf.readTString(); } }
],
TClonesArray(buf, list) {
if (!list._typename) list._typename = clTClonesArray;
list.$kind = clTClonesArray;
list.name = '';
const ver = buf.last_read_version;
if (ver > 2) buf.classStreamer(list, clTObject);
if (ver > 1) list.name = buf.readTString();
let classv = buf.readTString(), clv = 0;
const pos = classv.lastIndexOf(';');
if (pos > 0) {
clv = Number.parseInt(classv.slice(pos + 1));
classv = classv.slice(0, pos);
}
let nobjects = buf.ntou4();
if (nobjects < 0) nobjects = -nobjects; // for backward compatibility
list.arr = new Array(nobjects);
list.fLast = nobjects - 1;
list.fLowerBound = buf.ntou4();
let streamer = buf.fFile.getStreamer(classv, { val: clv });
streamer = buf.fFile.getSplittedStreamer(streamer);
if (!streamer)
console.log(`Cannot get member-wise streamer for ${classv}:${clv}`);
else {
// create objects
for (let n = 0; n < nobjects; ++n)
list.arr[n] = { _typename: classv };
// call streamer for all objects member-wise
for (let k = 0; k < streamer.length; ++k) {
for (let n = 0; n < nobjects; ++n)
streamer[k].func(buf, list.arr[n]);
}
}
},
TMap(buf, map) {
if (!map._typename) map._typename = clTMap;
map.name = '';
map.arr = [];
const ver = buf.last_read_version;
if (ver > 2) buf.classStreamer(map, clTObject);
if (ver > 1) map.name = buf.readTString();
const nobjects = buf.ntou4();
// create objects
for (let n = 0; n < nobjects; ++n) {
const obj = { _typename: 'TPair' };
obj.first = buf.readObjectAny();
obj.second = buf.readObjectAny();
if (obj.first) map.arr.push(obj);
}
},
TTreeIndex(buf, obj) {
const ver = buf.last_read_version;
obj._typename = 'TTreeIndex';
buf.classStreamer(obj, 'TVirtualIndex');
obj.fMajorName = buf.readTString();
obj.fMinorName = buf.readTString();
obj.fN = buf.ntoi8();
obj.fIndexValues = buf.readFastArray(obj.fN, kLong64);
if (ver > 1) obj.fIndexValuesMinor = buf.readFastArray(obj.fN, kLong64);
obj.fIndex = buf.readFastArray(obj.fN, kLong64);
},
TRefArray(buf, obj) {
obj._typename = 'TRefArray';
buf.classStreamer(obj, clTObject);
obj.name = buf.readTString();
const nobj = buf.ntoi4();
obj.fLast = nobj - 1;
obj.fLowerBound = buf.ntoi4();
/* const pidf = */ buf.ntou2();
obj.fUIDs = buf.readFastArray(nobj, kUInt);
},
TCanvas(buf, obj) {
obj._typename = clTCanvas;
buf.classStreamer(obj, clTPad);
obj.fDISPLAY = buf.readTString();
obj.fDoubleBuffer = buf.ntoi4();
obj.fRetained = (buf.ntou1() !== 0);
obj.fXsizeUser = buf.ntoi4();
obj.fYsizeUser = buf.ntoi4();
obj.fXsizeReal = buf.ntoi4();
obj.fYsizeReal = buf.ntoi4();
obj.fWindowTopX = buf.ntoi4();
obj.fWindowTopY = buf.ntoi4();
obj.fWindowWidth = buf.ntoi4();
obj.fWindowHeight = buf.ntoi4();
obj.fCw = buf.ntou4();
obj.fCh = buf.ntou4();
obj.fCatt = buf.classStreamer({}, clTAttCanvas);
buf.ntou1(); // ignore b << TestBit(kMoveOpaque);
buf.ntou1(); // ignore b << TestBit(kResizeOpaque);
obj.fHighLightColor = buf.ntoi2();
obj.fBatch = (buf.ntou1() !== 0);
buf.ntou1(); // ignore b << TestBit(kShowEventStatus);
buf.ntou1(); // ignore b << TestBit(kAutoExec);
buf.ntou1(); // ignore b << TestBit(kMenuBar);
},
TObjArray(buf, list) {
if (!list._typename) list._typename = clTObjArray;
list.$kind = clTObjArray;
list.name = '';
const ver = buf.last_read_version;
if (ver > 2)
buf.classStreamer(list, clTObject);
if (ver > 1)
list.name = buf.readTString();
const nobjects = buf.ntou4();
let i = 0;
list.arr = new Array(nobjects);
list.fLast = nobjects - 1;
list.fLowerBound = buf.ntou4();
while (i < nobjects)
list.arr[i++] = buf.readObjectAny();
},
TPolyMarker3D(buf, marker) {
const ver = buf.last_read_version;
buf.classStreamer(marker, clTObject);
buf.classStreamer(marker, clTAttMarker);
marker.fN = buf.ntoi4();
marker.fP = buf.readFastArray(marker.fN * 3, kFloat);
marker.fOption = buf.readTString();
marker.fName = (ver > 1) ? buf.readTString() : clTPolyMarker3D;
},
TPolyLine3D(buf, obj) {
buf.classStreamer(obj, clTObject);
buf.classStreamer(obj, clTAttLine);
obj.fN = buf.ntoi4();
obj.fP = buf.readFastArray(obj.fN * 3, kFloat);
obj.fOption = buf.readTString();
},
TStreamerInfo(buf, obj) {
buf.classStreamer(obj, clTNamed);
obj.fCheckSum = buf.ntou4();
obj.fClassVersion = buf.ntou4();
obj.fElements = buf.readObjectAny();
},
TStreamerElement(buf, element) {
const ver = buf.last_read_version;
buf.classStreamer(element, clTNamed);
element.fType = buf.ntou4();
element.fSize = buf.ntou4();
element.fArrayLength = buf.ntou4();
element.fArrayDim = buf.ntou4();
element.fMaxIndex = buf.readFastArray((ver === 1) ? buf.ntou4() : 5, kUInt);
element.fTypeName = buf.readTString();
if ((element.fType === kUChar) && ((element.fTypeName === 'Bool_t') || (element.fTypeName === 'bool')))
element.fType = kBool;
element.fXmin = element.fXmax = element.fFactor = 0;
if (ver === 3) {
element.fXmin = buf.ntod();
element.fXmax = buf.ntod();
element.fFactor = buf.ntod();
} else if ((ver > 3) && (element.fBits & BIT(6))) { // kHasRange
let p1 = element.fTitle.indexOf('[');
if ((p1 >= 0) && (element.fType > kOffsetP))
p1 = element.fTitle.indexOf('[', p1 + 1);
const p2 = element.fTitle.indexOf(']', p1 + 1);
if ((p1 >= 0) && (p2 >= p1 + 2)) {
const arr = element.fTitle.slice(p1+1, p2).split(',');
let nbits = 32;
if (!arr || arr.length < 2)
throw new Error(`Problem to decode range setting from streamer element title ${element.fTitle}`);
if (arr.length === 3) nbits = parseInt(arr[2]);
if (!Number.isInteger(nbits) || (nbits < 2) || (nbits > 32)) nbits = 32;
const parse_range = val => {
if (!val) return 0;
if (val.indexOf('pi') < 0) return parseFloat(val);
val = val.trim();
let sign = 1;
if (val[0] === '-') { sign = -1; val = val.slice(1); }
switch (val) {
case '2pi':
case '2*pi':
case 'twopi': return sign * 2 * Math.PI;
case 'pi/2': return sign * Math.PI / 2;
case 'pi/4': return sign * Math.PI / 4;
}
return sign * Math.PI;
};
element.fXmin = parse_range(arr[0]);
element.fXmax = parse_range(arr[1]);
// avoid usage of 1 << nbits, while only works up to 32 bits
const bigint = ((nbits >= 0) && (nbits < 32)) ? Math.pow(2, nbits) : 0xffffffff;
if (element.fXmin < element.fXmax)
element.fFactor = bigint / (element.fXmax - element.fXmin);
else if (nbits < 15)
element.fXmin = nbits;
}
}
},
TStreamerBase(buf, elem) {
const ver = buf.last_read_version;
buf.classStreamer(elem, clTStreamerElement);
if (ver > 2) elem.fBaseVersion = buf.ntou4();
},
TStreamerSTL(buf, elem) {
buf.classStreamer(elem, clTStreamerElement);
elem.fSTLtype = buf.ntou4();
elem.fCtype = buf.ntou4();
if ((elem.fSTLtype === kSTLmultimap) &&
((elem.fTypeName.indexOf('std::set') === 0) ||
(elem.fTypeName.indexOf('set') === 0))) elem.fSTLtype = kSTLset;
if ((elem.fSTLtype === kSTLset) &&
((elem.fTypeName.indexOf('std::multimap') === 0) ||
(elem.fTypeName.indexOf('multimap') === 0))) elem.fSTLtype = kSTLmultimap;
},
TStreamerSTLstring(buf, elem) {
if (buf.last_read_version > 0)
buf.classStreamer(elem, clTStreamerSTL);
},
TList(buf, obj) {
// stream all objects in the list from the I/O buffer
if (!obj._typename) obj._typename = this.typename;
obj.$kind = clTList; // all derived classes will be marked as well
if (buf.last_read_version > 3) {
buf.classStreamer(obj, clTObject);
obj.name = buf.readTString();
const nobjects = buf.ntou4();
obj.arr = new Array(nobjects);
obj.opt = new Array(nobjects);
for (let i = 0; i < nobjects; ++i) {
obj.arr[i] = buf.readObjectAny();
obj.opt[i] = buf.readTString();
}
} else {
obj.name = '';
obj.arr = [];
obj.opt = [];
}
},
THashList: clTList,
TStreamerLoop(buf, elem) {
if (buf.last_read_version > 1) {
buf.classStreamer(elem, clTStreamerElement);
elem.fCountVersion = buf.ntou4();
elem.fCountName = buf.readTString();
elem.fCountClass = buf.readTString();
}
},
TStreamerBasicPointer: 'TStreamerLoop',
TStreamerObject(buf, elem) {
if (buf.last_read_version > 1)
buf.classStreamer(elem, clTStreamerElement);
},
TStreamerBasicType: clTStreamerObject,
TStreamerObjectAny: clTStreamerObject,
TStreamerString: clTStreamerObject,
TStreamerObjectPointer: clTStreamerObject,
TStreamerObjectAnyPointer(buf, elem) {
if (buf.last_read_version > 0)
buf.classStreamer(elem, clTStreamerElement);
},
TTree: {
name: '$file',
func(buf, obj) { obj.$kind = 'TTree'; obj.$file = buf.fFile; }
},
RooRealVar(buf, obj) {
const v = buf.last_read_version;
buf.classStreamer(obj, 'RooAbsRealLValue');
if (v === 1) { buf.ntod(); buf.ntod(); buf.ntoi4(); } // skip fitMin, fitMax, fitBins
obj._error = buf.ntod();
obj._asymErrLo = buf.ntod();
obj._asymErrHi = buf.ntod();
if (v >= 2) obj._binning = buf.readObjectAny();
if (v === 3) obj._sharedProp = buf.readObjectAny();
if (v >= 4) obj._sharedProp = buf.classStreamer({}, 'RooRealVarSharedProperties');
},
RooAbsBinning(buf, obj) {
buf.classStreamer(obj, (buf.last_read_version === 1) ? clTObject : clTNamed);
buf.classStreamer(obj, 'RooPrintable');
},
RooCategory(buf, obj) {
const v = buf.last_read_version;
buf.classStreamer(obj, 'RooAbsCategoryLValue');
obj._sharedProp = (v === 1) ? buf.readObjectAny() : buf.classStreamer({}, 'RooCategorySharedProperties');
},
'RooWorkspace::CodeRepo': (buf /* , obj */) => {
const sz = (buf.last_read_version === 2) ? 3 : 2;
for (let i = 0; i < sz; ++i) {
let cnt = buf.ntoi4() * ((i === 0) ? 4 : 3);
while (cnt--) buf.readTString();
}
},
RooLinkedList(buf, obj) {
const v = buf.last_read_version;
buf.classStreamer(obj, clTObject);
let size = buf.ntoi4();
obj.arr = create(clTList);
while (size--)
obj.arr.Add(buf.readObjectAny());
if (v > 1) obj._name = buf.readTString();
},
TImagePalette: [
{
basename: clTObject, base: 1, func(buf, obj) {
if (!obj._typename) obj._typename = clTImagePalette;
buf.classStreamer(obj, clTObject);
}
},
{ name: 'fNumPoints', func(buf, obj) { obj.fNumPoints = buf.ntou4(); } },
{ name: 'fPoints', func(buf, obj) { obj.fPoints = buf.readFastArray(obj.fNumPoints, kDouble); } },
{ name: 'fColorRed', func(buf, obj) { obj.fColorRed = buf.readFastArray(obj.fNumPoints, kUShort); } },
{ name: 'fColorGreen', func(buf, obj) { obj.fColorGreen = buf.readFastArray(obj.fNumPoints, kUShort); } },
{ name: 'fColorBlue', func(buf, obj) { obj.fColorBlue = buf.readFastArray(obj.fNumPoints, kUShort); } },
{ name: 'fColorAlpha', func(buf, obj) { obj.fColorAlpha = buf.readFastArray(obj.fNumPoints, kUShort); } }
],
TAttImage: [
{ name: 'fImageQuality', func(buf, obj) { obj.fImageQuality = buf.ntoi4(); } },
{ name: 'fImageCompression', func(buf, obj) { obj.fImageCompression = buf.ntou4(); } },
{ name: 'fConstRatio', func(buf, obj) { obj.fConstRatio = (buf.ntou1() !== 0); } },
{ name: 'fPalette', func(buf, obj) { obj.fPalette = buf.classStreamer({}, clTImagePalette); } }
],
TASImage(buf, obj) {
if ((buf.last_read_version === 1) && (buf.fFile.fVersion > 0) && (buf.fFile.fVersion < 50000))
return console.warn('old TASImage version - not yet supported');
buf.classStreamer(obj, clTNamed);
if (buf.ntou1() !== 0) {
const size = buf.ntoi4();
obj.fPngBuf = buf.readFastArray(size, kUChar);
} else {
buf.classStreamer(obj, 'TAttImage');
obj.fWidth = buf.ntoi4();
obj.fHeight = buf.ntoi4();
obj.fImgBuf = buf.readFastArray(obj.fWidth * obj.fHeight, kDouble);
}
},
TMaterial(buf, obj) {
const v = buf.last_read_version;
buf.classStreamer(obj, clTNamed);
obj.fNumber = buf.ntoi4();
obj.fA = buf.ntof();
obj.fZ = buf.ntof();
obj.fDensity = buf.ntof();
if (v > 2) {
buf.classStreamer(obj, clTAttFill);
obj.fRadLength = buf.ntof();
obj.fInterLength = buf.ntof();
} else
obj.fRadLength = obj.fInterLength = 0;
},
TMixture(buf, obj) {
buf.classStreamer(obj, 'TMaterial');
obj.fNmixt = buf.ntoi4();
obj.fAmixt = buf.readFastArray(buf.ntoi4(), kFloat);
obj.fZmixt = buf.readFastArray(buf.ntoi4(), kFloat);
obj.fWmixt = buf.readFastArray(buf.ntoi4(), kFloat);
},
TVirtualPerfStats: clTObject, // use directly TObject streamer
TMethodCall: clTObject
};
/** @summary Add custom streamer
* @public */
function addUserStreamer(type, user_streamer) {
CustomStreamers[type] = user_streamer;
}
/** @summary these are streamers which do not handle version regularly
* @desc used for special classes like TRef or TBasket
* @private */
const DirectStreamers = {
// do nothing for these classes
TQObject() {},
TGraphStruct() {},
TGraphNode() {},
TGraphEdge() {},
TDatime(buf, obj) {
obj.fDatime = buf.ntou4();
},
TKey(buf, key) {
key.fNbytes = buf.ntoi4();
key.fVersion = buf.ntoi2();
key.fObjlen = buf.ntou4();
key.fDatime = buf.classStreamer({}, clTDatime);
key.fKeylen = buf.ntou2();
key.fCycle = buf.ntou2();
if (key.fVersion > 1000) {
key.fSeekKey = buf.ntou8();
buf.shift(8); // skip seekPdir
} else {
key.fSeekKey = buf.ntou4();
buf.shift(4); // skip seekPdir
}
key.fClassName = buf.readTString();
key.fName = buf.readTString();
key.fTitle = buf.readTString();
},
TDirectory(buf, dir) {
const version = buf.ntou2();
dir.fDatimeC = buf.classStreamer({}, clTDatime);
dir.fDatimeM = buf.classStreamer({}, clTDatime);
dir.fNbytesKeys = buf.ntou4();
dir.fNbytesName = buf.ntou4();
dir.fSeekDir = (version > 1000) ? buf.ntou8() : buf.ntou4();
dir.fSeekParent = (version > 1000) ? buf.ntou8() : buf.ntou4();
dir.fSeekKeys = (version > 1000) ? buf.ntou8() : buf.ntou4();
// if ((version % 1000) > 2) buf.shift(18); // skip fUUID
},
TRef(buf, obj) {
buf.classStreamer(obj, clTObject);
if (obj.fBits & kHasUUID)
obj.fUUID = buf.readTString();
else
obj.fPID = buf.ntou2();
},
'TMatrixTSym<float>': (buf, obj) => {
buf.classStreamer(obj, 'TMatrixTBase<float>');
obj.fElements = new Float32Array(obj.fNelems);
const arr = buf.readFastArray((obj.fNrows * (obj.fNcols + 1)) / 2, kFloat);
for (let i = 0, cnt = 0; i < obj.fNrows; ++i) {
for (let j = i; j < obj.fNcols; ++j)
obj.fElements[j * obj.fNcols + i] = obj.fElements[i * obj.fNcols + j] = arr[cnt++];
}
},
'TMatrixTSym<double>': (buf, obj) => {
buf.classStreamer(obj, 'TMatrixTBase<double>');
obj.fElements = new Float64Array(obj.fNelems);
const arr = buf.readFastArray((obj.fNrows * (obj.fNcols + 1)) / 2, kDouble);
for (let i = 0, cnt = 0; i < obj.fNrows; ++i) {
for (let j = i; j < obj.fNcols; ++j)
obj.fElements[j * obj.fNcols + i] = obj.fElements[i * obj.fNcols + j] = arr[cnt++];
}
}
};
/** @summary Returns type id by its name
* @private */
function getTypeId(typname, norecursion) {
switch (typname) {
case 'bool':
case 'Bool_t': return kBool;
case 'char':
case 'signed char':
case 'Char_t': return kChar;
case 'Color_t':
case 'Style_t':
case 'Width_t':
case 'short':
case 'Short_t': return kShort;
case 'int':
case 'EErrorType':
case 'Int_t': return kInt;
case 'long':
case 'Long_t': return kLong;
case 'float':
case 'Float_t': return kFloat;
case 'double':
case 'Double_t': return kDouble;
case 'unsigned char':
case 'UChar_t': return kUChar;
case 'unsigned short':
case 'UShort_t': return kUShort;
case 'unsigned':
case 'unsigned int':
case 'UInt_t': return kUInt;
case 'unsigned long':
case 'ULong_t': return kULong;
case 'int64_t':
case 'long long':
case 'Long64_t': return kLong64;
case 'uint64_t':
case 'unsigned long long':
case 'ULong64_t': return kULong64;
case 'Double32_t': return kDouble32;
case 'Float16_t': return kFloat16;
case 'char*':
case 'const char*':
case 'const Char_t*': return kCharStar;
}
if (!norecursion) {
const replace = CustomStreamers[typname];
if (isStr(replace)) return getTypeId(replace, true);
}
return -1;
}
/** @summary Analyze and returns arrays kind
* @return 0 if TString (or equivalent), positive value - some basic type, -1 - any other kind
* @private */
function getArrayKind(type_name) {
if ((type_name === clTString) || (type_name === 'string') ||
(CustomStreamers[type_name] === clTString)) return 0;
if ((type_name.length < 7) || (type_name.indexOf('TArray') !== 0)) return -1;
if (type_name.length === 7) {
switch (type_name[6]) {
case 'I': return kInt;
case 'D': return kDouble;
case 'F': return kFloat;
case 'S': return kShort;
case 'C': return kChar;
case 'L': return kLong;
default: return -1;
}
}
return type_name === 'TArrayL64' ? kLong64 : -1;
}
// eslint-disable-next-line prefer-const
let createPairStreamer;
/** @summary create element of the streamer
* @private */
function createStreamerElement(name, typename, file) {
const elem = {
_typename: clTStreamerElement, fName: name, fTypeName: typename,
fType: 0, fSize: 0, fArrayLength: 0, fArrayDim: 0, fMaxIndex: [0, 0, 0, 0, 0],
fXmin: 0, fXmax: 0, fFactor: 0
};
if (isStr(typename)) {
elem.fType = getTypeId(typename);
if ((elem.fType < 0) && file && file.fBasicTypes[typename])
elem.fType = file.fBasicTypes[typename];
} else {
elem.fType = typename;
typename = elem.fTypeName = BasicTypeNames[elem.fType] || 'int';
}
if (elem.fType > 0)
return elem; // basic type
// check if there are STL containers
const pos = typename.indexOf('<');
let stltype = kNotSTL;
if ((pos > 0) && (typename.indexOf('>') > pos + 2)) {
for (let stl = 1; stl < StlNames.length; ++stl) {
if (typename.slice(0, pos) === StlNames[stl]) {
stltype = stl; break;
}
}
}
if (stltype !== kNotSTL) {
elem._typename = clTStreamerSTL;
elem.fType = kStreamer;
elem.fSTLtype = stltype;
elem.fCtype = 0;
return elem;
}
if ((pos > 0) && (typename.slice(0, pos) === 'pair') && file && isFunc(createPairStreamer))
createPairStreamer(typename, file);
const isptr = typename.at(-1) === '*';
if (isptr)
elem.fTypeName = typename = typename.slice(0, typename.length - 1);
if (getArrayKind(typename) === 0) {
elem.fType = kTString;
return elem;
}
elem.fType = isptr ? kAnyP : kAny;
return elem;
}
/** @summary Function to read vector element in the streamer
* @private */
function readVectorElement(buf) {
if (this.member_wise) {
const n = buf.ntou4(), ver = this.stl_version;
if (n === 0)
return []; // for empty vector no need to search split streamers
if (n > 1000000)
throw new Error(`member-wise streaming of ${this.conttype} num ${n} member ${this.name}`);
let streamer;
if ((ver.val === this.member_ver) && (ver.checksum === this.member_checksum))
streamer = this.member_streamer;
else {
streamer = buf.fFile.getStreamer(this.conttype, ver);
this.member_streamer = streamer = buf.fFile.getSplittedStreamer(streamer);
this.member_ver = ver.val;
this.member_checksum = ver.checksum;
}
const res = new Array(n);
let i, k, member;
for (i = 0; i < n; ++i)
res[i] = { _typename: this.conttype }; // create objects
if (!streamer)
console.error(`Fail to create split streamer for ${this.conttype} need to read ${n} objects version ${ver}`);
else {
for (k = 0; k < streamer.length; ++k) {
member = streamer[k];
if (member.split_func)
member.split_func(buf, res, n);
else {
for (i = 0; i < n; ++i)
member.func(buf, res[i]);
}
}
}
return res;
}
const n = buf.ntou4(), res = new Array(n);
let i = 0;
if (n > 200000) {
console.error(`vector streaming for ${this.conttype} at ${n}`);
return res;
}
if (this.arrkind > 0)
while (i < n) res[i++] = buf.readFastArray(buf.ntou4(), this.arrkind);
else if (this.arrkind === 0)
while (i < n) res[i++] = buf.readTString();
else if (this.isptr)
while (i < n) res[i++] = buf.readObjectAny();
else if (this.submember)
while (i < n) res[i++] = this.submember.readelem(buf);
else
while (i < n) res[i++] = buf.classStreamer({}, this.conttype);
return res;
}
/** @summary Create streamer info for pair object
* @private */
createPairStreamer = function(typename, file) {
let si = file.findStreamerInfo(typename);
if (si)
return si;
let p1 = typename.indexOf('<');
const p2 = typename.lastIndexOf('>');
function getNextName() {
let res = '', p = p1 + 1, cnt = 0;
while ((p < p2) && (cnt >= 0)) {
switch (typename[p]) {
case '<': cnt++; break;
case ',': if (cnt === 0) cnt--; break;
case '>': cnt--; break;
}
if (cnt >= 0) res += typename[p];
p++;
}
p1 = p - 1;
return res.trim();
}
si = { _typename: 'TStreamerInfo', fClassVersion: 0, fName: typename, fElements: create(clTList) };
si.fElements.Add(createStreamerElement('first', getNextName(), file));
si.fElements.Add(createStreamerElement('second', getNextName(), file));
file.fStreamerInfos.arr.push(si);
return si;
};
/** @summary Function creates streamer for std::pair object
* @private */
function getPairStreamer(si, typname, file) {
if (!si)
si = createPairStreamer(typname, file);
const streamer = file.getStreamer(typname, null, si);
if (!streamer) return null;
if (streamer.length !== 2) {
console.error(`Streamer for pair class contains ${streamer.length} elements`);
return null;
}
for (let nn = 0; nn < 2; ++nn) {
if (streamer[nn].readelem && !streamer[nn].pair_name) {
streamer[nn].pair_name = (nn === 0) ? 'first' : 'second';
streamer[nn].func = function(buf, obj) {
obj[this.pair_name] = this.readelem(buf);
};
}
}
return streamer;
}
/** @summary Function used in streamer to read std::map object
* @private */
function readMapElement(buf) {
let streamer = this.streamer;
if (this.member_wise) {
// when member-wise streaming is used, version is written
const si = buf.fFile.findStreamerInfo(this.pairtype, this.stl_version.val, this.stl_version.checksum);
if (si && (this.si !== si)) {
streamer = getPairStreamer(si, this.pairtype, buf.fFile);
if (streamer?.length !== 2) {
console.log(`Fail to produce streamer for ${this.pairtype}`);
return null;
}
}
}
const n = buf.ntoi4(), res = new Array(n);
// no extra data written for empty map
if (n === 0)
return res;
if (this.member_wise && (buf.remain() >= 6)) {
if (buf.ntoi2() === kStreamedMemberWise)
buf.shift(4); // skip checksum
else
buf.shift(-2); // rewind
}
for (let i = 0; i < n; ++i) {
res[i] = { _typename: this.pairtype };
streamer[0].func(buf, res[i]);
if (!this.member_wise) streamer[1].func(buf, res[i]);
}
// due-to member-wise streaming second element read after first is completed
if (this.member_wise) {
if (buf.remain() >= 6) {
if (buf.ntoi2() === kStreamedMemberWise)
buf.shift(4); // skip checksum
else
buf.shift(-2); // rewind
}
for (let i = 0; i < n; ++i)
streamer[1].func(buf, res[i]);
}
return res;
}
/** @summary create member entry for streamer element
* @desc used for reading of data
* @private */
function createMemberStreamer(element, file) {
const member = {
name: element.fName, type: element.fType,
fArrayLength: element.fArrayLength,
fArrayDim: element.fArrayDim,
fMaxIndex: element.fMaxIndex
};
if (element.fTypeName === kBaseClass) {
if (getArrayKind(member.name) > 0) {
// this is workaround for arrays as base class
// we create 'fArray' member, which read as any other data member
member.name = 'fArray';
member.type = kAny;
} else {
// create streamer for base class
member.type = kBase;
// this.getStreamer(element.fName);
}
}
switch (member.type) {
case kBase:
member.base = element.fBaseVersion; // indicate base class
member.basename = element.fName; // keep class name
member.func = function(buf, obj) { buf.classStreamer(obj, this.basename); };
break;
case kShort:
member.func = function(buf, obj) { obj[this.name] = buf.ntoi2(); }; break;
case kInt:
case kCounter:
member.func = function(buf, obj) { obj[this.name] = buf.ntoi4(); }; break;
case kLong:
case kLong64:
member.func = function(buf, obj) { obj[this.name] = buf.ntoi8(); }; break;
case kDouble:
member.func = function(buf, obj) { obj[this.name] = buf.ntod(); }; break;
case kFloat:
member.func = function(buf, obj) { obj[this.name] = buf.ntof(); }; break;
case kLegacyChar:
case kUChar:
member.func = function(buf, obj) { obj[this.name] = buf.ntou1(); }; break;
case kUShort:
member.func = function(buf, obj) { obj[this.name] = buf.ntou2(); }; break;
case kBits:
case kUInt:
member.func = function(buf, obj) { obj[this.name] = buf.ntou4(); }; break;
case kULong64:
case kULong:
member.func = function(buf, obj) { obj[this.name] = buf.ntou8(); }; break;
case kBool:
member.func = function(buf, obj) { obj[this.name] = buf.ntou1() !== 0; }; break;
case kOffsetL + kBool:
case kOffsetL + kInt:
case kOffsetL + kCounter:
case kOffsetL + kDouble:
case kOffsetL + kUChar:
case kOffsetL + kShort:
case kOffsetL + kUShort:
case kOffsetL + kBits:
case kOffsetL + kUInt:
case kOffsetL + kULong:
case kOffsetL + kULong64:
case kOffsetL + kLong:
case kOffsetL + kLong64:
case kOffsetL + kFloat:
if (element.fArrayDim < 2) {
member.arrlength = element.fArrayLength;
member.func = function(buf, obj) {
obj[this.name] = buf.readFastArray(this.arrlength, this.type - kOffsetL);
};
} else {
member.arrlength = element.fMaxIndex[element.fArrayDim - 1];
member.minus1 = true;
member.func = function(buf, obj) {
obj[this.name] = buf.readNdimArray(this, (buf2, handle) =>
buf2.readFastArray(handle.arrlength, handle.type - kOffsetL));
};
}
break;
case kOffsetL + kChar:
if (element.fArrayDim < 2) {
member.arrlength = element.fArrayLength;
member.func = function(buf, obj) {
obj[this.name] = buf.readFastString(this.arrlength);
};
} else {
member.minus1 = true; // one dimension used for char*
member.arrlength = element.fMaxIndex[element.fArrayDim - 1];
member.func = function(buf, obj) {
obj[this.name] = buf.readNdimArray(this, (buf2, handle) =>
buf2.readFastString(handle.arrlength));
};
}
break;
case kOffsetP + kBool:
case kOffsetP + kInt:
case kOffsetP + kDouble:
case kOffsetP + kUChar:
case kOffsetP + kShort:
case kOffsetP + kUShort:
case kOffsetP + kBits:
case kOffsetP + kUInt:
case kOffsetP + kULong:
case kOffsetP + kULong64:
case kOffsetP + kLong:
case kOffsetP + kLong64:
case kOffsetP + kFloat:
member.cntname = element.fCountName;
member.func = function(buf, obj) {
obj[this.name] = (buf.ntou1() === 1) ? buf.readFastArray(obj[this.cntname], this.type - kOffsetP) : [];
};
break;
case kOffsetP + kChar:
member.cntname = element.fCountName;
member.func = function(buf, obj) {
obj[this.name] = (buf.ntou1() === 1) ? buf.readFastString(obj[this.cntname]) : null;
};
break;
case kDouble32:
case kOffsetL + kDouble32:
case kOffsetP + kDouble32:
member.double32 = true;
// eslint-disable-next-line no-fallthrough
case kFloat16:
case kOffsetL + kFloat16:
case kOffsetP + kFloat16:
if (element.fFactor !== 0) {
member.factor = 1 / element.fFactor;
member.min = element.fXmin;
member.read = function(buf) { return buf.ntou4() * this.factor + this.min; };
} else
if ((element.fXmin === 0) && member.double32)
member.read = function(buf) { return buf.ntof(); };
else {
member.nbits = Math.round(element.fXmin);
if (member.nbits === 0) member.nbits = 12;
member.dv = new DataView(new ArrayBuffer(8), 0); // used to cast from uint32 to float32
member.read = function(buf) {
const theExp = buf.ntou1(), theMan = buf.ntou2();
this.dv.setUint32(0, (theExp << 23) | ((theMan & ((1 << (this.nbits + 1)) - 1)) << (23 - this.nbits)));
return ((1 << (this.nbits + 1) & theMan) ? -1 : 1) * this.dv.getFloat32(0);
};
}
member.readarr = function(buf, len) {
const arr = this.double32 ? new Float64Array(len) : new Float32Array(len);
for (let n = 0; n < len; ++n) arr[n] = this.read(buf);
return arr;
};
if (member.type < kOffsetL)
member.func = function(buf, obj) { obj[this.name] = this.read(buf); };
else
if (member.type > kOffsetP) {
member.cntname = element.fCountName;
member.func = function(buf, obj) {
obj[this.name] = (buf.ntou1() === 1) ? this.readarr(buf, obj[this.cntname]) : null;
};
} else
if (element.fArrayDim < 2) {
member.arrlength = element.fArrayLength;
member.func = function(buf, obj) { obj[this.name] = this.readarr(buf, this.arrlength); };
} else {
member.arrlength = element.fMaxIndex[element.fArrayDim - 1];
member.minus1 = true;
member.func = function(buf, obj) {
obj[this.name] = buf.readNdimArray(this, (buf2, handle) => handle.readarr(buf2, handle.arrlength));
};
}
break;
case kAnyP:
case kObjectP:
member.func = function(buf, obj) {
obj[this.name] = buf.readNdimArray(this, buf2 => buf2.readObjectAny());
};
break;
case kAny:
case kAnyp:
case kObjectp:
case kObject: {
let classname = (element.fTypeName === kBaseClass) ? element.fName : element.fTypeName;
if (classname.at(-1) === '*')
classname = classname.slice(0, classname.length - 1);
const arrkind = getArrayKind(classname);
if (arrkind > 0) {
member.arrkind = arrkind;
member.func = function(buf, obj) { obj[this.name] = buf.readFastArray(buf.ntou4(), this.arrkind); };
} else if (arrkind === 0)
member.func = function(buf, obj) { obj[this.name] = buf.readTString(); };
else {
member.classname = classname;
if (element.fArrayLength > 1) {
member.func = function(buf, obj) {
obj[this.name] = buf.readNdimArray(this, (buf2, handle) => buf2.classStreamer({}, handle.classname));
};
} else {
member.func = function(buf, obj) {
obj[this.name] = buf.classStreamer({}, this.classname);
};
}
}
break;
}
case kOffsetL + kObject:
case kOffsetL + kAny:
case kOffsetL + kAnyp:
case kOffsetL + kObjectp: {
let classname = element.fTypeName;
if (classname.at(-1) === '*')
classname = classname.slice(0, classname.length - 1);
member.arrkind = getArrayKind(classname);
if (member.arrkind < 0) member.classname = classname;
member.func = function(buf, obj) {
obj[this.name] = buf.readNdimArray(this, (buf2, handle) => {
if (handle.arrkind > 0)
return buf2.readFastArray(buf.ntou4(), handle.arrkind);
if (handle.arrkind === 0)
return buf2.readTString();
return buf2.classStreamer({}, handle.classname);
});
};
break;
}
case kChar:
member.func = function(buf, obj) { obj[this.name] = buf.ntoi1(); }; break;
case kCharStar:
member.func = function(buf, obj) {
const len = buf.ntoi4();
obj[this.name] = buf.substring(buf.o, buf.o + len);
buf.o += len;
};
break;
case kTString:
member.func = function(buf, obj) { obj[this.name] = buf.readTString(); };
break;
case kTObject:
case kTNamed:
member.typename = element.fTypeName;
member.func = function(buf, obj) { obj[this.name] = buf.classStreamer({}, this.typename); };
break;
case kOffsetL + kTString:
case kOffsetL + kTObject:
case kOffsetL + kTNamed:
member.typename = element.fTypeName;
member.func = function(buf, obj) {
const ver = buf.readVersion();
obj[this.name] = buf.readNdimArray(this, (buf2, handle) => {
if (handle.typename === clTString)
return buf2.readTString();
return buf2.classStreamer({}, handle.typename);
});
buf.checkByteCount(ver, this.typename + '[]');
};
break;
case kStreamLoop:
case kOffsetL + kStreamLoop:
member.typename = element.fTypeName;
member.cntname = element.fCountName;
if (member.typename.lastIndexOf('**') > 0) {
member.typename = member.typename.slice(0, member.typename.lastIndexOf('**'));
member.isptrptr = true;
} else {
member.typename = member.typename.slice(0, member.typename.lastIndexOf('*'));
member.isptrptr = false;
}
if (member.isptrptr)
member.readitem = function(buf) { return buf.readObjectAny(); };
else {
member.arrkind = getArrayKind(member.typename);
if (member.arrkind > 0)
member.readitem = function(buf) { return buf.readFastArray(buf.ntou4(), this.arrkind); };
else if (member.arrkind === 0)
member.readitem = function(buf) { return buf.readTString(); };
else
member.readitem = function(buf) { return buf.classStreamer({}, this.typename); };
}
if (member.readitem !== undefined) {
member.read_loop = function(buf, cnt) {
return buf.readNdimArray(this, (buf2, member2) => {
const itemarr = new Array(cnt);
for (let i = 0; i < cnt; ++i)
itemarr[i] = member2.readitem(buf2);
return itemarr;
});
};
member.func = function(buf, obj) {
const ver = buf.readVersion(),
res = this.read_loop(buf, obj[this.cntname]);
obj[this.name] = buf.checkByteCount(ver, this.typename) ? res : null;
};
member.branch_func = function(buf, obj) {
// this is special functions, used by branch in the STL container
const ver = buf.readVersion(), sz0 = obj[this.stl_size], res = new Array(sz0);
for (let loop0 = 0; loop0 < sz0; ++loop0) {
const cnt = obj[this.cntname][loop0];
res[loop0] = this.read_loop(buf, cnt);
}
obj[this.name] = buf.checkByteCount(ver, this.typename) ? res : null;
};
member.objs_branch_func = function(buf, obj) {
// special function when branch read as part of complete object
// objects already preallocated and only appropriate member must be set
// see code in JSRoot.tree.js for reference
const ver = buf.readVersion(),
arr = obj[this.name0]; // objects array where reading is done
for (let loop0 = 0; loop0 < arr.length; ++loop0) {
const obj1 = this.get(arr, loop0), cnt = obj1[this.cntname];
obj1[this.name] = this.read_loop(buf, cnt);
}
buf.checkByteCount(ver, this.typename);
};
} else {
console.error(`fail to provide function for ${element.fName} (${element.fTypeName}) typ = ${element.fType}`);
member.func = function(buf, obj) {
const ver = buf.readVersion();
buf.checkByteCount(ver);
obj[this.name] = null;
};
}
break;
case kStreamer: {
member.typename = element.fTypeName;
const stl = (element.fSTLtype || 0) % 40;
if ((element._typename === 'TStreamerSTLstring') ||
(member.typename === 'string') || (member.typename === 'string*'))
member.readelem = buf => buf.readTString();
else if ((stl === kSTLvector) || (stl === kSTLlist) ||
(stl === kSTLdeque) || (stl === kSTLset) || (stl === kSTLmultiset)) {
const p1 = member.typename.indexOf('<'),
p2 = member.typename.lastIndexOf('>');
member.conttype = member.typename.slice(p1 + 1, p2).trim();
member.typeid = getTypeId(member.conttype);
if ((member.typeid < 0) && file.fBasicTypes[member.conttype]) {
member.typeid = file.fBasicTypes[member.conttype];
console.log(`!!! Reuse basic type ${member.conttype} from file streamer infos`);
}
// check
if (element.fCtype && (element.fCtype < 20) && (element.fCtype !== member.typeid)) {
console.warn(`Contained type ${member.conttype} not recognized as basic type ${element.fCtype} FORCE`);
member.typeid = element.fCtype;
}
if (member.typeid > 0) {
member.readelem = function(buf) {
return buf.readFastArray(buf.ntoi4(), this.typeid);
};
} else {
member.isptr = false;
if (member.conttype.at(-1) === '*') {
member.isptr = true;
member.conttype = member.conttype.slice(0, member.conttype.length - 1);
}
if (element.fCtype === kObjectp) member.isptr = true;
member.arrkind = getArrayKind(member.conttype);
member.readelem = readVectorElement;
if (!member.isptr && (member.arrkind < 0)) {
const subelem = createStreamerElement('temp', member.conttype, file);
if (subelem.fType === kStreamer) {
subelem.$fictional = true;
member.submember = createMemberStreamer(subelem, file);
}
}
}
} else if ((stl === kSTLmap) || (stl === kSTLmultimap)) {
const p1 = member.typename.indexOf('<'),
p2 = member.typename.lastIndexOf('>');
member.pairtype = 'pair<' + member.typename.slice(p1 + 1, p2) + '>';
// remember found streamer info from the file -
// most probably it is the only one which should be used
member.si = file.findStreamerInfo(member.pairtype);
member.streamer = getPairStreamer(member.si, member.pairtype, file);
if (!member.streamer || (member.streamer.length !== 2)) {
console.error(`Fail to build streamer for pair ${member.pairtype}`);
delete member.streamer;
}
if (member.streamer) member.readelem = readMapElement;
} else if (stl === kSTLbitset)
member.readelem = (buf /* , obj */) => buf.readFastArray(buf.ntou4(), kBool);
if (!member.readelem) {
console.error(`failed to create streamer for element ${member.typename} ${member.name} element ${element._typename} STL type ${element.fSTLtype}`);
member.func = function(buf, obj) {
const ver = buf.readVersion();
buf.checkByteCount(ver);
obj[this.name] = null;
};
} else
if (!element.$fictional) {
member.read_version = function(buf, cnt) {
if (cnt === 0) return null;
const ver = buf.readVersion();
this.member_wise = ((ver.val & kStreamedMemberWise) !== 0);
this.stl_version = undefined;
if (this.member_wise) {
ver.val &= ~kStreamedMemberWise;
this.stl_version = { val: buf.ntoi2() };
if (this.stl_version.val <= 0) this.stl_version.checksum = buf.ntou4();
}
return ver;
};
member.func = function(buf, obj) {
const ver = this.read_version(buf);
let res = buf.readNdimArray(this, (buf2, member2) => member2.readelem(buf2));
if (!buf.checkByteCount(ver, this.typename)) res = null;
obj[this.name] = res;
};
member.branch_func = function(buf, obj) {
// special function to read data from STL branch
const cnt = obj[this.stl_size],
ver = this.read_version(buf, cnt),
arr = new Array(cnt);
for (let n = 0; n < cnt; ++n)
arr[n] = buf.readNdimArray(this, (buf2, member2) => member2.readelem(buf2));
if (ver) buf.checkByteCount(ver, `branch ${this.typename}`);
obj[this.name] = arr;
};
member.split_func = function(buf, arr, n) {
// function to read array from member-wise streaming
const ver = this.read_version(buf);
for (let i = 0; i < n; ++i)
arr[i][this.name] = buf.readNdimArray(this, (buf2, member2) => member2.readelem(buf2));
buf.checkByteCount(ver, this.typename);
};
member.objs_branch_func = function(buf, obj) {
// special function when branch read as part of complete object
// objects already preallocated and only appropriate member must be set
// see code in JSRoot.tree.js for reference
const arr = obj[this.name0], // objects array where reading is done
ver = this.read_version(buf, arr.length);
for (let n = 0; n < arr.length; ++n) {
const obj1 = this.get(arr, n);
obj1[this.name] = buf.readNdimArray(this, (buf2, member2) => member2.readelem(buf2));
}
if (ver) buf.checkByteCount(ver, `branch ${this.typename}`);
};
}
break;
}
default:
console.error(`fail to provide function for ${element.fName} (${element.fTypeName}) typ = ${element.fType}`);
member.func = function(/* buf, obj */) {}; // do nothing, fix in the future
}
return member;
}
/** @summary Let directly assign methods when doing I/O
* @private */
function addClassMethods(clname, streamer) {