UNPKG

jsroot

Version:
1,431 lines (1,270 loc) 144 kB
import { createHttpRequest, BIT, internals, settings, browser, create, getMethods, addMethods, isNodeJs, isObject, isFunc, isStr, clTObject, clTNamed, clTString, clTObjString, clTKey, clTFile, clTTree, clTList, clTMap, clTObjArray, clTClonesArray, clTAttLine, clTAttFill, clTAttMarker, clTStyle, clTImagePalette, atob_func, clTPad, clTCanvas, clTAttCanvas, clTPolyMarker3D, clTF1, clTF12, clTF2, clTF3 } 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 */ /* eslint-disable one-var */ const 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.ntobool(); 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.ntobool(); 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 = clTTree; obj.$file = buf.fFile; } }, TBranch(buf, obj) { const v = buf.last_read_version; if (v > 9) buf.streamClassMembers(obj, 'TBranch', v); else { buf.classStreamer(obj, clTNamed); if (v > 7) buf.classStreamer(obj, clTAttFill); obj.fCompress = buf.ntoi4(); obj.fBasketSize = buf.ntoi4(); obj.fEntryOffsetLen = buf.ntoi4(); obj.fWriteBasket = buf.ntoi4(); obj.fEntryNumber = buf.ntoi4(); obj.fOffset = buf.ntoi4(); obj.fMaxBaskets = buf.ntoi4(); if (v > 6) obj.fSplitLevel = buf.ntoi4(); obj.fEntries = buf.ntod(); obj.fTotBytes = buf.ntod(); obj.fZipBytes = buf.ntod(); obj.fBranches = buf.classStreamer({}, clTObjArray); obj.fLeaves = buf.classStreamer({}, clTObjArray); obj.fBaskets = buf.classStreamer({}, clTObjArray); buf.ntoi1(); // isArray obj.fBasketBytes = buf.readFastArray(obj.fMaxBaskets, kInt); buf.ntoi1(); // isArray obj.fBasketEntry = buf.readFastArray(obj.fMaxBaskets, kInt); const isArray = buf.ntoi1(); obj.fBasketSeek = buf.readFastArray(obj.fMaxBaskets, isArray === 2 ? kLong64 : kInt); obj.fFileName = buf.readTString(); } }, 'ROOT::RNTuple': { name: '$file', func(buf, obj) { obj.$kind = 'ROOT::RNTuple'; obj.$file = buf.fFile; } }, RooRealVar(buf, obj) { const v = buf.last_read_version; buf.classStreamer(obj, 'RooAbsRealLValue'); if (v === 1) { // skip fitMin, fitMax, fitBins buf.ntod(); buf.ntod(); buf.ntoi4(); } 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.ntobool(); } }, { 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.ntobool()) { 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')) 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.ntobool(); }; 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) { 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 = Boolean(ver.val & kStreamedMemberWise); 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), res = buf.readNdimArray(this, (buf2, member2) => member2.readelem(buf2));