From 63f4fb10a508f08fe99efdc66127121a64d9c9e1 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:14:49 +0100 Subject: [PATCH 01/11] [rntuple] first std::bitset implementation --- modules/rntuple.mjs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index bc85d8d66..d4fd9b1d1 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -1132,6 +1132,38 @@ class ArrayReaderItem extends ReaderItem { } +/** @class reading of std::bitset + * @desc large numbers with more than 48 bits converted to BigInt + * @private */ + +class BitsetReaderItem extends ReaderItem { + + constructor(items, tgtname, size) { + super(items, tgtname); + this.size = size; + items[0].set_not_simple(); + this.bigint = size > 48; + } + + func(tgtobj) { + const tmp = {}; + let len = 0, res = 0; + while (len < this.size) { + this.items[0].func(tmp); + if (tmp.bit) + res |= 1 << len; + len++; + } + tgtobj[this.name] = res; + } + + shift(entries) { + this.items[0].shift(entries * this.size); + } + +} + + /** @class reading std::vector and other kinds of collections * @private */ @@ -1370,6 +1402,12 @@ async function rntupleProcess(rntuple, selector, args = {}) { return new StringReaderItem([itemlen, itemstr], tgtname); } + if ((columns.length === 1) && (field.typeName.indexOf('std::bitset') === 0)) { + const itembit = addColumnReadout(columns[0], 'bit'); + return new BitsetReaderItem([itembit], tgtname, Number(field.arraySize)); + } + + let is_stl = false; ['vector', 'map', 'unordered_map', 'multimap', 'unordered_multimap', 'set', 'unordered_set', 'multiset', 'unordered_multiset'].forEach(name => { if (field.typeName.indexOf('std::' + name) === 0) From e9e48275e38a0349958beae7b883387d38d4d70c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:25:40 +0100 Subject: [PATCH 02/11] [rntuple] check for normal bitset --- demo/node/rntuple_test.js | 68 +++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/demo/node/rntuple_test.js b/demo/node/rntuple_test.js index fbc05f35f..ab1575d4e 100644 --- a/demo/node/rntuple_test.js +++ b/demo/node/rntuple_test.js @@ -5,73 +5,79 @@ import { readHeaderFooter, rntupleProcess } from 'jsroot/rntuple'; console.log(`JSROOT version ${version}`); -let filename = './rntuple_test.root'; +let filename = './rntuple_test.root', + any_error = false; + if (process?.argv && process.argv[2]) filename = process.argv[2]; const file = await openFile(filename), - rntuple = await file.readObject('Data'); - -await readHeaderFooter(rntuple); + rntuple = await file.readObject('Data'), + builder = await readHeaderFooter(rntuple); console.log('Performing Validations and debugging info'); -if (rntuple.builder?.name !== 'Data') +if (builder?.name !== 'Data') console.error('FAILURE: name differs from expected'); else - console.log('OK: name is', rntuple.builder?.name); + console.log('OK: name is', builder?.name); -if (rntuple.builder?.description !== '') +if (builder.description !== '') console.error('FAILURE: description should be the empty string'); else console.log('OK: description is empty string'); -if (rntuple.builder?.xxhash3 === undefined || rntuple.builder.xxhash3 === null) +if (builder.xxhash3 === undefined || builder.xxhash3 === null) console.warn('WARNING: xxhash3 is missing'); else - console.log('OK: xxhash3 is', '0x' + rntuple.builder.xxhash3.toString(16).padStart(16, '0')); + console.log('OK: xxhash3 is', '0x' + builder.xxhash3.toString(16).padStart(16, '0')); // Fields Check -if (!rntuple.builder?.fieldDescriptors?.length) +if (!builder?.fieldDescriptors?.length) console.error('FAILURE: No fields deserialized'); else { - console.log(`OK: ${rntuple.builder.fieldDescriptors.length} field(s) deserialized`); - for (let i = 0; i < rntuple.builder.fieldDescriptors.length; ++i) { - const field = rntuple.builder.fieldDescriptors[i]; + console.log(`OK: ${builder.fieldDescriptors.length} field(s) deserialized`); + for (let i = 0; i < builder.fieldDescriptors.length; ++i) { + const field = builder.fieldDescriptors[i]; if (!field.fieldName || !field.typeName) console.error(`FAILURE: Field ${i} is missing name or type`); else console.log(`OK: Field ${i}: ${field.fieldName} (${field.typeName})`); if (i === 0) { - if (field.fieldName !== 'IntField' || field.typeName !== 'std::int32_t') + if (field.fieldName !== 'IntField' || field.typeName !== 'std::int32_t') { + any_error = true; console.error(`FAILURE: First field should be 'IntField (std::int32_t)' but got '${field.fieldName} (${field.typeName})'`); - } else if (i === rntuple.builder.fieldDescriptors.length - 1) { - if (field.fieldName !== '_1' || field.typeName !== 'bool') + } + } else if (i === builder.fieldDescriptors.length - 1) { + if (field.fieldName !== '_1' || field.typeName !== 'bool') { + any_error = true; console.error(`FAILURE: Last field should be '_1 (bool)' but got '${field.fieldName} (${field.typeName})'`); + } } } } // Column Check -if (!rntuple.builder?.columnDescriptors?.length) +if (!rntuple.builder?.columnDescriptors?.length) { + any_error = true; console.error('FAILURE: No columns deserialized'); -else { - console.log(`OK: ${rntuple.builder.columnDescriptors.length} column(s) deserialized`); - for (let i = 0; i < rntuple.builder.columnDescriptors.length; ++i) { - const column = rntuple.builder.columnDescriptors[i]; +} else { + console.log(`OK: ${builder.columnDescriptors.length} column(s) deserialized`); + for (let i = 0; i < builder.columnDescriptors.length; ++i) { + const column = builder.columnDescriptors[i]; if (column.fieldId === undefined || column.fieldId === null) console.error(`FAILURE: Column ${i} is missing fieldId`); else console.log(`OK: Column ${i} fieldId: ${column.fieldId} `); - if (i === 0) { - if (column.fieldId !== 0) - console.error('FAILURE: First column should be for fieldId 0'); - } else if (i === rntuple.builder.columnDescriptors.length - 1) { - if (column.fieldId !== 38) - console.error('FAILURE: Last column should be for fieldId 38'); + if ((i === 0) && (column.fieldId !== 0)) { + any_error = true; + console.error('FAILURE: First column should be for fieldId 0'); + } else if ((i === builder.columnDescriptors.length - 1) && (column.fieldId !== builder.fieldDescriptors.length - 1)) { + any_error = true; + console.error(`FAILURE: Last column should be for fieldId ${builder.fieldDescriptors.length - 1}`); } } } @@ -81,9 +87,10 @@ const selector = new TSelector(), fields = ['IntField', 'FloatField', 'DoubleField', 'Float16Field', 'Real32Trunc', 'Real32Quant', 'StringField', 'BoolField', + 'BitsetField', 'ArrayInt', 'VariantField', 'TupleField', 'VectString', 'VectInt', 'VectBool', 'Vect2Float', 'Vect2Bool', 'MultisetField', - 'MapStringFloat', 'MapIntDouble', 'MapStringBool'], + 'MapStringFloat', 'MapIntDouble', 'MapStringBool' ], epsilonValues = { Real32Trunc: 0.3, Real32Quant: 1e-4, Float16Field: 1e-2 }; for (const f of fields) @@ -97,8 +104,6 @@ selector.Begin = () => { // Now validate entry data const EPSILON = 1e-7; -let any_error = false; - function compare(expected, value, eps) { if (typeof expected === 'number') return Math.abs(value - expected) < (eps ?? EPSILON); @@ -144,7 +149,8 @@ selector.Process = function(entryIndex) { MultisetField: [], MapStringFloat: [], MapIntDouble: [], - MapStringBool: [] + MapStringBool: [], + BitsetField: 1 << entryIndex * 3 % 25 }, npx = (entryIndex + 5) % 7; switch (entryIndex % 3) { From da9b4fe542265dbd1fdc3e12190eaf4cb167b731 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:37:52 +0100 Subject: [PATCH 03/11] [rntuple] read large bitset as BigInt --- modules/rntuple.mjs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index d4fd9b1d1..917ced099 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -1147,11 +1147,15 @@ class BitsetReaderItem extends ReaderItem { func(tgtobj) { const tmp = {}; - let len = 0, res = 0; + let len = 0, res = this.bigint ? 0n : 0; while (len < this.size) { this.items[0].func(tmp); - if (tmp.bit) - res |= 1 << len; + if (tmp.bit) { + if (this.bigint) + res |= (1n << BigInt(len)); + else + res |= 1 << len; + } len++; } tgtobj[this.name] = res; From 92b91d1c5c4ecad7189486e39b0e91d1372e5ed4 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:38:06 +0100 Subject: [PATCH 04/11] [rntuple] add testing of large bitset --- demo/node/rntuple_test.cxx | 13 +++++++++++++ demo/node/rntuple_test.js | 14 ++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/demo/node/rntuple_test.cxx b/demo/node/rntuple_test.cxx index cdb3253b0..1350ecb1e 100644 --- a/demo/node/rntuple_test.cxx +++ b/demo/node/rntuple_test.cxx @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef __ROOTCLING__ #pragma link C++ class std::map+; @@ -21,6 +22,8 @@ #pragma link C++ class std::multiset+; #pragma link C++ class std::variant+; #pragma link C++ class std::tuple+; +#pragma link C++ class std::bitset<25>+; +#pragma link C++ class std::bitset<117>+; #endif @@ -55,6 +58,8 @@ void rntuple_test() auto VariantField = model->MakeField>("VariantField"); auto TupleField = model->MakeField>("TupleField"); auto ArrayInt = model->MakeField>("ArrayInt"); + auto BitsetField = model->MakeField>("BitsetField"); + auto LargeBitsetField = model->MakeField>("LargeBitsetField"); auto VectString = model->MakeField>("VectString"); auto VectInt = model->MakeField>("VectInt"); auto VectBool = model->MakeField>("VectBool"); @@ -103,6 +108,14 @@ void rntuple_test() Vect2Float->clear(); Vect2Bool->clear(); + BitsetField->reset(); + LargeBitsetField->reset(); + + BitsetField->set(i * 3 % 25, true); + + LargeBitsetField->set((i + 7) % 117, true); + LargeBitsetField->set((i + 35) % 117, true); + int npx = (i + 5) % 7; for (int j = 0; j < npx; ++j) { VectString->emplace_back("str_" + std::to_string(j)); diff --git a/demo/node/rntuple_test.js b/demo/node/rntuple_test.js index ab1575d4e..0276b2cff 100644 --- a/demo/node/rntuple_test.js +++ b/demo/node/rntuple_test.js @@ -87,8 +87,7 @@ const selector = new TSelector(), fields = ['IntField', 'FloatField', 'DoubleField', 'Float16Field', 'Real32Trunc', 'Real32Quant', 'StringField', 'BoolField', - 'BitsetField', - 'ArrayInt', 'VariantField', 'TupleField', + 'ArrayInt', 'BitsetField', 'LargeBitsetField', 'VariantField', 'TupleField', 'VectString', 'VectInt', 'VectBool', 'Vect2Float', 'Vect2Bool', 'MultisetField', 'MapStringFloat', 'MapIntDouble', 'MapStringBool' ], epsilonValues = { Real32Trunc: 0.3, Real32Quant: 1e-4, Float16Field: 1e-2 }; @@ -139,6 +138,8 @@ selector.Process = function(entryIndex) { StringField: `entry_${entryIndex}`, BoolField: entryIndex % 3 === 1, ArrayInt: [entryIndex + 1, entryIndex + 2, entryIndex + 3, entryIndex + 4, entryIndex + 5], + BitsetField: 1 << entryIndex * 3 % 25, + LargeBitsetField: (1n << BigInt((entryIndex + 7) % 117)) | (1n << BigInt((entryIndex + 35) % 117)), VariantField: null, TupleField: { _0: `tuple_${entryIndex}`, _1: entryIndex*3, _2: (entryIndex % 3 === 1) }, VectString: [], @@ -149,8 +150,7 @@ selector.Process = function(entryIndex) { MultisetField: [], MapStringFloat: [], MapIntDouble: [], - MapStringBool: [], - BitsetField: 1 << entryIndex * 3 % 25 + MapStringBool: [] }, npx = (entryIndex + 5) % 7; switch (entryIndex % 3) { @@ -189,8 +189,10 @@ selector.Process = function(entryIndex) { if (!compare(expected, value, epsilonValues[field])) { console.error(`FAILURE: ${field} at entry ${entryIndex} expected ${JSON.stringify(expected)}, got ${JSON.stringify(value)}`); any_error = true; - } else - console.log(`OK: ${field} at entry ${entryIndex} = ${JSON.stringify(value)}`); + } else { + const res = typeof value === 'bigint' ? value.toString() + 'n' : JSON.stringify(value); + console.log(`OK: ${field} at entry ${entryIndex} = ${res}`); + } } catch (err) { console.error(`ERROR: Failed to read ${field} at entry ${entryIndex}: ${err.message}`); any_error = true; From dd911f16b475538155edbc7534833255b602f8aa Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:42:17 +0100 Subject: [PATCH 05/11] Extra mark BigInt value in hpainter --- modules/gui/HierarchyPainter.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/gui/HierarchyPainter.mjs b/modules/gui/HierarchyPainter.mjs index 49c8e88c6..a76a4f06d 100644 --- a/modules/gui/HierarchyPainter.mjs +++ b/modules/gui/HierarchyPainter.mjs @@ -558,7 +558,11 @@ function objectHierarchy(top, obj, args = undefined) { } } } - } else if ((typeof fld === 'number') || (typeof fld === 'boolean') || (typeof fld === 'bigint')) { + } else if (typeof fld === 'bigint') { + simple = true; + item._value = fld.toString() + 'n'; + item._vclass = cssValueNum; + } else if ((typeof fld === 'number') || (typeof fld === 'boolean')) { simple = true; if (key === 'fBits') item._value = '0x' + fld.toString(16); From db23dc257e698ef81fae2a21bd98a288c9e231d7 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:56:00 +0100 Subject: [PATCH 06/11] [rntuple] add atomic support --- modules/rntuple.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 917ced099..6b01f7884 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -1390,6 +1390,10 @@ async function rntupleProcess(rntuple, selector, args = {}) { return new ArrayReaderItem([item1], tgtname, Number(field.arraySize)); } + if ((childs.length === 1) && (field.typeName.indexOf('std::atomic') === 0)) + return addFieldReading(builder, childs[0], tgtname); + + if ((childs.length > 0) && (field.typeName.indexOf('std::tuple') === 0)) { const items = []; for (let i = 0; i < childs.length; ++i) From e2c17d87a46aeffa1e096c6902c61ac27091b05a Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 13:56:23 +0100 Subject: [PATCH 07/11] [rntuple] add atomic testing --- demo/node/rntuple_test.cxx | 3 +++ demo/node/rntuple_test.js | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/demo/node/rntuple_test.cxx b/demo/node/rntuple_test.cxx index 1350ecb1e..4a258013b 100644 --- a/demo/node/rntuple_test.cxx +++ b/demo/node/rntuple_test.cxx @@ -60,6 +60,7 @@ void rntuple_test() auto ArrayInt = model->MakeField>("ArrayInt"); auto BitsetField = model->MakeField>("BitsetField"); auto LargeBitsetField = model->MakeField>("LargeBitsetField"); + auto AtomicDoubleField = model->MakeField>("AtomicDoubleField"); auto VectString = model->MakeField>("VectString"); auto VectInt = model->MakeField>("VectInt"); auto VectBool = model->MakeField>("VectBool"); @@ -116,6 +117,8 @@ void rntuple_test() LargeBitsetField->set((i + 7) % 117, true); LargeBitsetField->set((i + 35) % 117, true); + *AtomicDoubleField = 111.444 * i; + int npx = (i + 5) % 7; for (int j = 0; j < npx; ++j) { VectString->emplace_back("str_" + std::to_string(j)); diff --git a/demo/node/rntuple_test.js b/demo/node/rntuple_test.js index 0276b2cff..519875ca6 100644 --- a/demo/node/rntuple_test.js +++ b/demo/node/rntuple_test.js @@ -87,7 +87,8 @@ const selector = new TSelector(), fields = ['IntField', 'FloatField', 'DoubleField', 'Float16Field', 'Real32Trunc', 'Real32Quant', 'StringField', 'BoolField', - 'ArrayInt', 'BitsetField', 'LargeBitsetField', 'VariantField', 'TupleField', + 'ArrayInt', 'BitsetField', 'LargeBitsetField', + 'AtomicDoubleField', 'VariantField', 'TupleField', 'VectString', 'VectInt', 'VectBool', 'Vect2Float', 'Vect2Bool', 'MultisetField', 'MapStringFloat', 'MapIntDouble', 'MapStringBool' ], epsilonValues = { Real32Trunc: 0.3, Real32Quant: 1e-4, Float16Field: 1e-2 }; @@ -140,6 +141,7 @@ selector.Process = function(entryIndex) { ArrayInt: [entryIndex + 1, entryIndex + 2, entryIndex + 3, entryIndex + 4, entryIndex + 5], BitsetField: 1 << entryIndex * 3 % 25, LargeBitsetField: (1n << BigInt((entryIndex + 7) % 117)) | (1n << BigInt((entryIndex + 35) % 117)), + AtomicDoubleField: entryIndex * 111.444, VariantField: null, TupleField: { _0: `tuple_${entryIndex}`, _1: entryIndex*3, _2: (entryIndex % 3 === 1) }, VectString: [], From 5ef506ac68bcbd5ec93e24d21b52c176cee6af7c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 14:13:28 +0100 Subject: [PATCH 08/11] [rntuple] support custom class field For now only simple case --- modules/rntuple.mjs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 6b01f7884..dc61a7ee3 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -1215,7 +1215,6 @@ class VariantReaderItem extends ReaderItem { constructor(items, tgtname) { super(items, tgtname); - this.items = items; this.set_not_simple(); } @@ -1249,6 +1248,31 @@ class TupleReaderItem extends ReaderItem { } +/** @class reading std::tuple<> field + * @private */ + +class CustomClassReaderItem extends ReaderItem { + + constructor(items, tgtname, classname) { + super(items, tgtname); + this.classname = classname; + this.set_not_simple(); + } + + func(tgtobj) { + const obj = { _typename: this.classname }; + this.items.forEach(item => item.func(obj)); + tgtobj[this.name] = obj; + } + + shift(entries) { + this.items.forEach(item => item.shift(entries)); + } + +} + + + /** @class reading std::pair field * @private */ @@ -1401,6 +1425,13 @@ async function rntupleProcess(rntuple, selector, args = {}) { return new TupleReaderItem(items, tgtname); } + if ((childs.length > 0) && field.checksum && field.typeName) { + const items = []; + for (let i = 0; i < childs.length; ++i) + items.push(addFieldReading(builder, childs[i], childs[i].fieldName)); + return new CustomClassReaderItem(items, tgtname, field.typeName); + } + throw new Error(`No columns found for field '${field.fieldName}' in RNTuple`); } From 1ef2f2a1c52a5cf3cf5ac4d4042f28824ffb638c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 14:16:07 +0100 Subject: [PATCH 09/11] [rntuple] add testing of custom class Include updated reference file --- demo/node/rntuple_test.cxx | 14 ++++++++++++++ demo/node/rntuple_test.js | 3 ++- demo/node/rntuple_test.root | Bin 3895 -> 4610 bytes 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/demo/node/rntuple_test.cxx b/demo/node/rntuple_test.cxx index 4a258013b..cad46b363 100644 --- a/demo/node/rntuple_test.cxx +++ b/demo/node/rntuple_test.cxx @@ -15,6 +15,14 @@ #include #include +class TestClass { + public: + std::string fName; + std::string fTitle; + double fValue{0.}; + +}; + #ifdef __ROOTCLING__ #pragma link C++ class std::map+; #pragma link C++ class std::map+; @@ -24,6 +32,7 @@ #pragma link C++ class std::tuple+; #pragma link C++ class std::bitset<25>+; #pragma link C++ class std::bitset<117>+; +#pragma link C++ class TestClass+; #endif @@ -61,6 +70,7 @@ void rntuple_test() auto BitsetField = model->MakeField>("BitsetField"); auto LargeBitsetField = model->MakeField>("LargeBitsetField"); auto AtomicDoubleField = model->MakeField>("AtomicDoubleField"); + auto TestClassField = model->MakeField("TestClassField"); auto VectString = model->MakeField>("VectString"); auto VectInt = model->MakeField>("VectInt"); auto VectBool = model->MakeField>("VectBool"); @@ -96,6 +106,10 @@ void rntuple_test() *TupleField = { std::string("tuple_") + std::to_string(i), i * 3, (i % 3 == 1) }; + TestClassField->fName = "name_" + std::to_string(i); + TestClassField->fTitle = "title_" + std::to_string(i); + TestClassField->fValue = i; + VectString->clear(); VectInt->clear(); VectBool->clear(); diff --git a/demo/node/rntuple_test.js b/demo/node/rntuple_test.js index 519875ca6..5442eaeec 100644 --- a/demo/node/rntuple_test.js +++ b/demo/node/rntuple_test.js @@ -88,7 +88,7 @@ const selector = new TSelector(), 'Float16Field', 'Real32Trunc', 'Real32Quant', 'StringField', 'BoolField', 'ArrayInt', 'BitsetField', 'LargeBitsetField', - 'AtomicDoubleField', 'VariantField', 'TupleField', + 'AtomicDoubleField', 'TestClassField', 'VariantField', 'TupleField', 'VectString', 'VectInt', 'VectBool', 'Vect2Float', 'Vect2Bool', 'MultisetField', 'MapStringFloat', 'MapIntDouble', 'MapStringBool' ], epsilonValues = { Real32Trunc: 0.3, Real32Quant: 1e-4, Float16Field: 1e-2 }; @@ -142,6 +142,7 @@ selector.Process = function(entryIndex) { BitsetField: 1 << entryIndex * 3 % 25, LargeBitsetField: (1n << BigInt((entryIndex + 7) % 117)) | (1n << BigInt((entryIndex + 35) % 117)), AtomicDoubleField: entryIndex * 111.444, + TestClassField: { _typename: 'TestClass', fName: `name_${entryIndex}`, fTitle: `title_${entryIndex}`, fValue: entryIndex }, VariantField: null, TupleField: { _0: `tuple_${entryIndex}`, _1: entryIndex*3, _2: (entryIndex % 3 === 1) }, VectString: [], diff --git a/demo/node/rntuple_test.root b/demo/node/rntuple_test.root index a610d96d8476167f25fbe1084da800757763a8a8..9c576ecffdf4fc8e9970a32fa18de2c88bafa12b 100644 GIT binary patch delta 2721 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 delta 2052 zcmZ`(dstH07T>%iNl@_-nlFshj8eSfs~Q0@U!`6RD$VjiW$Q;NWR8Ou(6j} zn)a~hT0T-Qm09-GEUjy4DmCR)W~n(DN;wCd=KK0%*7vRRTW7EHTYG^ZkhuX(|y)wUyM?kS_|95`aUU z)7R)ye4iP}?v7*|{IVu4w6Mtz<~Se$$gquuj|4I8X0nNnx-RkaF$Ci9sy)Wtu<#6w0ZDGuz zxKAlj-B(4wb<$(>e2KiOwvN-3>vyOnOvmt{CbE3U?aT4&xuIN4drM@?>%ov|C;nXG z|168;TT8gBEN@iO9_yT$<;d7=)zv$xSbFvlnSriR4Tt(e!VmognQt#E?~4*93N}@) zrKP@gYmqx_znmh$HtJ)%w^ChR^G#S916tL$5Z{J-^`9 z>_hJWQx9_T!j|}EygM_hpoGbI`{{*9X<1c|4pA9n5yG0)%Fx@lpqwZr>q<;b>Rogo ze6sgd#C^X$&f>0~7S7ADj77hiCm${4FFIdVQfD$w6FrF7%XlqmTIVzMqVaeB-a2G( zhsvwj5x=|1V>VauAp7*01NJIGO7peZQ^vxipCQsY(WyrdZzc)uBYVaJW7lsQc**(f zO>ADI$}06zlV2r1u@3rL<;Z3=tPr|dbX2oN4>l|@=ic4P-<~~cBFlA}l6P|>=SIJ< z5O3T_albPvmj3*V&?&dTs7re>`KdkJcH+C;2c^UdC=As1`rNXik#mjhyGkRg64nd= z1|&8NG2G}<7IpFSw*?wNk1t-Y-DJvTzO+c)REXzXdQk2cL!w%OTXi#8S-#iLd#WFb#0l#lz+`X&vy6lCdS z3eMOZ03CO&#P(?=>ZsK;EFX0IUlQbD!dL=fEc}K<>(n@5{)?;feqf?2Kk5Ivic#(zcKuDy-4T@@P55W_$W5-srN+Txp{1Bn4Zth!n$E zn>_ipfGcp{qUw7XMCWhtpY_Z(zJ3SJ(9CFEpiiaCf_T;yXe~} zm_lS4su9C0a?`6fS0xkH_OGucE+huL^hMXzyWku~$M^T#s!*fsr}vDaYX1;@ z@xemfU+4NwF&9Giwb5?Mki?;|v6GDTUW3O6s`4f-dI$|cTgKxG! zEZ4oW%4)#AH%O8v&aOe=X6L4%U`-``&rI%%T!~iv$J$EP&K5 zBEF{(B$xynN;AV@Bb3gd-r^Yq;eX)?4KiT2eZF-%xZ0BqhsoN_ynfsak12X?Xjug5 z>&*G*!&s3JVhtHRs3rSvs8gQ?(GXw|J@bDJqL%3bS-T58ncqoLt7^XafjIzB zCP)Qn@=@9F@Z4w1J3ogSDDo=F^Hx*Bo+Q;a6y88d0*-RHkuv1RTbz`s6J(lsrQTNZ zdELzHaH;%Ol*<#z&RIvcx$3Ha{d#9h!&5mmkSUSuv7>*gT1BDRbU=^N&v4A|5q?_2 zTPe!w45QT|;VHp$ICZHDk z|NH3M`J1zHik9^!C$7wCBBh?=wq6)&+V6g#nBegWk8-e4BBj#4WLrwbI`p63AiaXI{NNA=m)@`Qnx9q From f02f13d046825e2f34d670dab8f1144c55245f0f Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 14:18:08 +0100 Subject: [PATCH 10/11] Update comment --- changes.md | 5 ++++- modules/rntuple.mjs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/changes.md b/changes.md index 31d666a98..4fbc1f077 100644 --- a/changes.md +++ b/changes.md @@ -3,13 +3,16 @@ ## Changes in dev 1. Implement new data types in `RNtuple` + - reduced float types kFloat16, kReal32Trunc, kReal32Quant - `std::vector` - `std::map`, `std::unordered_map`, `std::multimap`, `std::unordered_multimap` with `std::pair` - `std::set`, `std::unordered_set`, `std::multiset`, `std::unordered_multiset` - `std::array` - `std::variant` - `std::tuple` - - kFloat16, kReal32Trunc, kReal32Quant + - `std::bitset` + - `std::atomic` + - simple custom classes 1. Resort order of ranges in http request, fixing several long-standing problems #374 1. Implement for `TPie` 3d, text, title drawing including interactivity 1. Implement `TCanvas` support in `build3d` function #373 diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index dc61a7ee3..65dbbdcd1 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -1248,7 +1248,7 @@ class TupleReaderItem extends ReaderItem { } -/** @class reading std::tuple<> field +/** @class reading custom class field * @private */ class CustomClassReaderItem extends ReaderItem { From 5c13465f88b3b5c28f8e684599dbbe43c9cca9c3 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 19 Feb 2026 14:21:46 +0100 Subject: [PATCH 11/11] Fix eslint warning --- modules/rntuple.mjs | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 65dbbdcd1..4e73fa164 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -1272,8 +1272,6 @@ class CustomClassReaderItem extends ReaderItem { } - - /** @class reading std::pair field * @private */