From e9775ad6420f2be27d7918a9a342de159c2b6134 Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Thu, 12 Jun 2025 13:02:54 +0530 Subject: [PATCH 1/7] Add initial check for field list frame type in header deserialization --- modules/rntuple.mjs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index d2cde094c..57a8a8d67 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -98,35 +98,34 @@ class RBufferReader { class RNTupleDescriptorBuilder { - deserializeHeader(header_blob) { +deserializeHeader(header_blob) { if (!header_blob) return; const reader = new RBufferReader(header_blob); - // 1. Read header version (4 bytes) + // Step 1: Read basic metadata this.version = reader.readU32(); - - // 2. Read feature flags (4 bytes) this.headerFeatureFlags = reader.readU32(); - - // 3. Read xxhash3 (64-bit, 8 bytes) this.xxhash3 = reader.readU64(); - - // 4. Read name (length-prefixed string) this.name = reader.readString(); - - // 5. Read description (length-prefixed string) this.description = reader.readString(); - + this.library = reader.readString(); - // Console output to verify deserialization results console.log('Version:', this.version); console.log('Header Feature Flags:', this.headerFeatureFlags); - console.log('xxhash3:', '0x' + this.xxhash3.toString(16).padStart(16, '0')); + console.log('XXHash3:', this.xxhash3); console.log('Name:', this.name); console.log('Description:', this.description); - } + // Step 2: Read and handle field list frame + this.fieldListSize = reader.readS64(); + const fieldListIsList = this.fieldListSize < 0; + + if (fieldListIsList) + console.log('Field list is a list frame'); + else + console.log('Field list is not a list frame'); +} deserializeFooter(footer_blob) { if (!footer_blob) return; From e11648f877dfc8f122efdccb0ea290f91c4e73fb Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Thu, 12 Jun 2025 14:00:05 +0530 Subject: [PATCH 2/7] parse record envelope and field list envelope from RNTuple header --- modules/rntuple.mjs | 67 +++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 57a8a8d67..05a214c66 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -27,6 +27,15 @@ class RBufferReader { this.offset += 1; return val; } + readU24() { + const b1 = this.view.getUint8(this.offset); // MSB + const b2 = this.view.getUint8(this.offset + 1); + const b3 = this.view.getUint8(this.offset + 2); // LSB + this.offset += 3; + return (b1 << 16) | (b2 << 8) | b3; +} + + // Read unsigned 16-bit integer (2 BYTES) readU16() { @@ -103,28 +112,50 @@ deserializeHeader(header_blob) { const reader = new RBufferReader(header_blob); - // Step 1: Read basic metadata - this.version = reader.readU32(); - this.headerFeatureFlags = reader.readU32(); - this.xxhash3 = reader.readU64(); - this.name = reader.readString(); - this.description = reader.readString(); - this.library = reader.readString(); + // Read base metadata + this.version = reader.readU32(); + this.headerFeatureFlags = reader.readU32(); + this.xxhash3 = reader.readU64(); + this.name = reader.readString(); + this.description = reader.readString(); + this.library = reader.readString(); console.log('Version:', this.version); - console.log('Header Feature Flags:', this.headerFeatureFlags); - console.log('XXHash3:', this.xxhash3); + console.log('Header Feature Flags:', '0x' + this.headerFeatureFlags.toString(16).padStart(8, '0')); + console.log('xxhash3:', '0x' + this.xxhash3.toString(16).padStart(16, '0')); console.log('Name:', this.name); console.log('Description:', this.description); - - // Step 2: Read and handle field list frame - this.fieldListSize = reader.readS64(); - const fieldListIsList = this.fieldListSize < 0; - - if (fieldListIsList) - console.log('Field list is a list frame'); - else - console.log('Field list is not a list frame'); + console.log('Library:', this.library); + + // Record Envelope + const recordEnvelopeType = reader.readU8(); + const recordEnvelopeSize = reader.readU24(); + const recordEnvelopeChecksum = reader.readU32(); + + console.log(`Record Envelope Type: ${recordEnvelopeType}`); + console.log(`Record Envelope Size: ${recordEnvelopeSize}`); + console.log(`Record Envelope Checksum: ${recordEnvelopeChecksum}`); + + // Field List Envelope + const fieldListType = reader.readU8(); + const fieldListSize = reader.readU24(); + const fieldListChecksum = reader.readU32(); + console.log(`Field List Envelope Type: ${fieldListType}`); + console.log(`Field List Size: ${fieldListSize}`); + console.log(`Field List Checksum: ${fieldListChecksum}`); + + // Validate envelope type before reading list frame + if (fieldListType !== 1) + console.warn('Unexpected field list envelope type:', fieldListType); + else + console.log('field list is a list frame'); + + // Field List Frame inside the envelope payload + const fieldListFrameSize = reader.readU32(); + const numFields = reader.readU32(); + + console.log(`Field List Frame Size: ${fieldListFrameSize}`); + console.log(`Number of Fields: ${numFields}`); } deserializeFooter(footer_blob) { From d79c587b7331ea01c195479c1bf9bc542276109f Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Thu, 12 Jun 2025 15:20:02 +0530 Subject: [PATCH 3/7] deserialize envelope, feature flags, and field list header from RNTuple header --- modules/rntuple.mjs | 107 ++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 59 deletions(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 05a214c66..03b356021 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -27,15 +27,6 @@ class RBufferReader { this.offset += 1; return val; } - readU24() { - const b1 = this.view.getUint8(this.offset); // MSB - const b2 = this.view.getUint8(this.offset + 1); - const b3 = this.view.getUint8(this.offset + 2); // LSB - this.offset += 3; - return (b1 << 16) | (b2 << 8) | b3; -} - - // Read unsigned 16-bit integer (2 BYTES) readU16() { @@ -101,61 +92,59 @@ class RBufferReader { this.offset += 8; return val; } - + } class RNTupleDescriptorBuilder { - + deserializeHeader(header_blob) { - if (!header_blob) return; - - const reader = new RBufferReader(header_blob); - - // Read base metadata - this.version = reader.readU32(); - this.headerFeatureFlags = reader.readU32(); - this.xxhash3 = reader.readU64(); - this.name = reader.readString(); - this.description = reader.readString(); - this.library = reader.readString(); - - console.log('Version:', this.version); - console.log('Header Feature Flags:', '0x' + this.headerFeatureFlags.toString(16).padStart(8, '0')); - console.log('xxhash3:', '0x' + this.xxhash3.toString(16).padStart(16, '0')); - console.log('Name:', this.name); - console.log('Description:', this.description); - console.log('Library:', this.library); - - // Record Envelope - const recordEnvelopeType = reader.readU8(); - const recordEnvelopeSize = reader.readU24(); - const recordEnvelopeChecksum = reader.readU32(); - - console.log(`Record Envelope Type: ${recordEnvelopeType}`); - console.log(`Record Envelope Size: ${recordEnvelopeSize}`); - console.log(`Record Envelope Checksum: ${recordEnvelopeChecksum}`); - - // Field List Envelope - const fieldListType = reader.readU8(); - const fieldListSize = reader.readU24(); - const fieldListChecksum = reader.readU32(); - console.log(`Field List Envelope Type: ${fieldListType}`); - console.log(`Field List Size: ${fieldListSize}`); - console.log(`Field List Checksum: ${fieldListChecksum}`); - - // Validate envelope type before reading list frame - if (fieldListType !== 1) - console.warn('Unexpected field list envelope type:', fieldListType); - else - console.log('field list is a list frame'); - - // Field List Frame inside the envelope payload - const fieldListFrameSize = reader.readU32(); - const numFields = reader.readU32(); - - console.log(`Field List Frame Size: ${fieldListFrameSize}`); - console.log(`Number of Fields: ${numFields}`); + if (!header_blob) return; + + const reader = new RBufferReader(header_blob), + packed = reader.readU64(); + + // Step 0: Envelope metadata + this.envelopeType = Number(packed & 0xFFFFn); + this.envelopeLength = Number((packed >> 16n) & 0xFFFFFFFFFFFFn); + + console.log('Envelope Type ID:', this.envelopeType); + console.log('Envelope Length:', this.envelopeLength); + + // const payloadStart = reader.offset;(will use this later for checksum in the end) + + // Read feature flags list (may span multiple 64-bit words) + this.featureFlags = []; + while (true) { + const val = reader.readU64(); + this.featureFlags.push(val); + if ((val & 0x8000000000000000n) === 0n) break; // MSB not set: end of list + } + + // verify all feature flags are zero + if (this.featureFlags.some(v => v !== 0n)) + console.warn('Unexpected non-zero feature flags:', this.featureFlags); + + // Read basic metadata strings + this.name = reader.readString(); + this.description = reader.readString(); + this.writer = reader.readString(); + + console.log('Name:', this.name); + console.log('Description:', this.description); + console.log('Writer:', this.writer); + + // Step 3: Parse the Field Record Frame header + this.fieldListSize = reader.readS64(); // signed 64-bit + const fieldListIsList = this.fieldListSize < 0; + + if (!fieldListIsList) + throw new Error('Field list frame is not a list frame, which is required.'); + else + console.log('Field list is a list frame with size:', this.fieldListSize); + + const fieldListCount = reader.readU32(); // number of field entries + console.log('Field List Count:', fieldListCount); } deserializeFooter(footer_blob) { From 321816afc5bf4e0f12d176b79227a5831eb0e08f Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Thu, 12 Jun 2025 19:02:17 +0530 Subject: [PATCH 4/7] Doing Suggested Changes in the code and removing CI for version in rntuple_test.js --- demo/node/rntuple_test.js | 5 ----- modules/rntuple.mjs | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/demo/node/rntuple_test.js b/demo/node/rntuple_test.js index 3712eb199..fc18361dd 100644 --- a/demo/node/rntuple_test.js +++ b/demo/node/rntuple_test.js @@ -18,11 +18,6 @@ if (rntuple.builder?.name !== 'Staff') else console.log('OK: name is', rntuple.builder?.name); -if (typeof rntuple.builder?.version !== 'number') - console.error('FAILURE: version is missing or invalid'); -else - console.log('OK: version is', rntuple.builder.version); - if (!rntuple.builder?.description) console.error('FAILURE: description is missing'); else diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 03b356021..243ec32c5 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -101,17 +101,19 @@ class RNTupleDescriptorBuilder { deserializeHeader(header_blob) { if (!header_blob) return; - const reader = new RBufferReader(header_blob), - packed = reader.readU64(); + const reader = new RBufferReader(header_blob); + const typeAndLength = reader.readU64(); - // Step 0: Envelope metadata - this.envelopeType = Number(packed & 0xFFFFn); - this.envelopeLength = Number((packed >> 16n) & 0xFFFFFFFFFFFFn); + // Envelope metadata + // The 16 bits are the envelope type ID, and the 48 bits are the envelope length + this.envelopeType = Number(typeAndLength & 0xFFFFn); + this.envelopeLength = Number((typeAndLength >> 16n) & 0xFFFFFFFFFFFFn); console.log('Envelope Type ID:', this.envelopeType); console.log('Envelope Length:', this.envelopeLength); - // const payloadStart = reader.offset;(will use this later for checksum in the end) + // TODO: Validate the envelope checksum at the end of deserialization + //const payloadStart = reader.offset; // Read feature flags list (may span multiple 64-bit words) this.featureFlags = []; @@ -123,13 +125,13 @@ deserializeHeader(header_blob) { // verify all feature flags are zero if (this.featureFlags.some(v => v !== 0n)) - console.warn('Unexpected non-zero feature flags:', this.featureFlags); + throw new Error('Unexpected non-zero feature flags: ' + this.featureFlags); // Read basic metadata strings this.name = reader.readString(); this.description = reader.readString(); this.writer = reader.readString(); - + // TODO: Remove debug logs before finalizing console.log('Name:', this.name); console.log('Description:', this.description); console.log('Writer:', this.writer); From 2948249d78e7550ddc488f2d1e41d66aed542651 Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Thu, 12 Jun 2025 22:14:32 +0530 Subject: [PATCH 5/7] deserialize each Field Record Frame and log field name with type --- modules/rntuple.mjs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 243ec32c5..1e7944faa 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -147,6 +147,24 @@ deserializeHeader(header_blob) { const fieldListCount = reader.readU32(); // number of field entries console.log('Field List Count:', fieldListCount); + for (let i = 0; i < fieldListCount; ++i) { + const fieldVersion = reader.readU32(), + typeVersion = reader.readU32(), + parentFieldId = reader.readU32(), + structRole = reader.readU8(), + flags = reader.readU8(), + + fieldName = reader.readString(), + typeName = reader.readString(), + typeAlias = reader.readString(), + description = reader.readString(); + + if (flags & 0x1) reader.readU32(); // ArraySize + if (flags & 0x2) reader.readU32(); // SourceFieldId + if (flags & 0x4) reader.readU32(); // Checksum + + console.log(`Field ${i + 1}:`, fieldName, "&&", typeName); +} } deserializeFooter(footer_blob) { From 7ae6a2c4ba48b2276b92d1ac8632fff29873335c Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Fri, 13 Jun 2025 14:11:57 +0530 Subject: [PATCH 6/7] Implement full deserialization logic for header and footer envelope and structured into modular functions --- modules/rntuple.mjs | 214 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 184 insertions(+), 30 deletions(-) diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index 1e7944faa..fb753b71d 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -70,6 +70,13 @@ class RBufferReader { return val; } + // Read 64-bit float (8 BYTES) + readF64() { + const val = this.view.getFloat64(this.offset, LITTLE_ENDIAN); + this.offset += 8; + return val; + } + // Read a string with 32-bit length prefix readString() { const length = this.readU32(); @@ -102,6 +109,53 @@ deserializeHeader(header_blob) { if (!header_blob) return; const reader = new RBufferReader(header_blob); + // Read the envelope metadata + this._readEnvelopeMetadata(reader); + + // TODO: Validate the envelope checksum at the end of deserialization + // const payloadStart = reader.offset; + + // Read feature flags list (may span multiple 64-bit words) + this._readFeatureFlags(reader); + + // Read metadata strings + this._readStrings(reader); + + // List frame: list of field record frames + this._readFieldDescriptors(reader); + // List frame: list of column record frames + this._readColumnDescriptors(reader); + // Read alias column descriptors + this._readAliasColumn(reader); + // Read Extra Type Information + this._readExtraTypeInformation(reader); +} + + +deserializeFooter(footer_blob) { + if (!footer_blob) return; + + const reader = new RBufferReader(footer_blob); + // Read the envelope metadata + this._readEnvelopeMetadata(reader); + + // Feature flag(32 bits) + this.footerFeatureFlags = reader.readU32(); + // Header checksum (64-bit xxhash3) + this.headerChecksum = reader.readU64(); + + // Schema extension record frame (4 list frames inside) + this._readFieldDescriptors(reader); + this._readColumnDescriptors(reader); + this._readAliasColumnDescriptors(reader); + this._readExtraTypeInfos(reader); + + // Cluster Group record frame + this._readClusterGroups(reader); + } + + +_readEnvelopeMetadata(reader) { const typeAndLength = reader.readU64(); // Envelope metadata @@ -111,11 +165,8 @@ deserializeHeader(header_blob) { console.log('Envelope Type ID:', this.envelopeType); console.log('Envelope Length:', this.envelopeLength); - - // TODO: Validate the envelope checksum at the end of deserialization - //const payloadStart = reader.offset; - - // Read feature flags list (may span multiple 64-bit words) +} +_readFeatureFlags(reader) { this.featureFlags = []; while (true) { const val = reader.readU64(); @@ -126,8 +177,8 @@ deserializeHeader(header_blob) { // verify all feature flags are zero if (this.featureFlags.some(v => v !== 0n)) throw new Error('Unexpected non-zero feature flags: ' + this.featureFlags); - - // Read basic metadata strings +} +_readStrings(reader) { this.name = reader.readString(); this.description = reader.readString(); this.writer = reader.readString(); @@ -135,50 +186,153 @@ deserializeHeader(header_blob) { console.log('Name:', this.name); console.log('Description:', this.description); console.log('Writer:', this.writer); - - // Step 3: Parse the Field Record Frame header +} +_readFieldDescriptors(reader) { this.fieldListSize = reader.readS64(); // signed 64-bit const fieldListIsList = this.fieldListSize < 0; if (!fieldListIsList) throw new Error('Field list frame is not a list frame, which is required.'); - else - console.log('Field list is a list frame with size:', this.fieldListSize); const fieldListCount = reader.readU32(); // number of field entries console.log('Field List Count:', fieldListCount); + + // List frame: list of field record frames + + const fieldDescriptors = []; for (let i = 0; i < fieldListCount; ++i) { const fieldVersion = reader.readU32(), typeVersion = reader.readU32(), parentFieldId = reader.readU32(), - structRole = reader.readU8(), - flags = reader.readU8(), + structRole = reader.readU16(), + flags = reader.readU16(), fieldName = reader.readString(), typeName = reader.readString(), typeAlias = reader.readString(), description = reader.readString(); - - if (flags & 0x1) reader.readU32(); // ArraySize - if (flags & 0x2) reader.readU32(); // SourceFieldId - if (flags & 0x4) reader.readU32(); // Checksum - - console.log(`Field ${i + 1}:`, fieldName, "&&", typeName); + let arraySize = null, sourceFieldId = null, checksum = null; + + if (flags & 0x1) arraySize = reader.readU32(); + if (flags & 0x2) sourceFieldId = reader.readU32(); + if (flags & 0x4) checksum = reader.readU32(); + + fieldDescriptors.push({ + fieldVersion, + typeVersion, + parentFieldId, + structRole, + flags, + fieldName, + typeName, + typeAlias, + description, + arraySize, + sourceFieldId, + checksum + }); + console.log(`Field ${i + 1}:`, fieldName, '&&', typeName); } + this.fieldDescriptors = fieldDescriptors; } - -deserializeFooter(footer_blob) { - if (!footer_blob) return; - - const reader = new RBufferReader(footer_blob); - - this.footerFeatureFlags = reader.readU32(); - this.headerChecksum = reader.readU32(); - - console.log('Footer Feature Flags:', this.footerFeatureFlags); - console.log('Header Checksum:', this.headerChecksum); +_readColumnDescriptors(reader) { + this.columnListSize = reader.readS64(); // signed 64-bit + const columnListIsList = this.columnListSize < 0; + if (!columnListIsList) + throw new Error('Column list frame is not a list frame, which is required.'); + const columnListCount = reader.readU32(); // number of column entries + console.log('Column List Count:', columnListCount); + const columnDescriptors = []; + for (let i = 0; i < columnListCount; ++i) { + const coltype = reader.readU16(), + bitsOnStrorage = reader.readU16(), + fieldId = reader.readU32(), + flags = reader.readU16(), + representationIndex = reader.readU16(); + + let firstElementIndex = null, minValue = null, maxValue = null; + if (flags & 0x1) firstElementIndex = reader.readU64(); + if (flags & 0x2){ + minValue = reader.readF64(); + maxValue = reader.readF64(); } + columnDescriptors.push({ + coltype, + bitsOnStrorage, + fieldId, + flags, + representationIndex, + firstElementIndex, + minValue, + maxValue, + isDeferred: (flags & 0x01) !== 0, + isSuppressed: (firstElementIndex !== null && firstElementIndex < 0) + }); + } + this.columnDescriptors = columnDescriptors; +} +_readAliasColumn(reader){ + this.aliasColumnListSize = reader.readS64(); // signed 64-bit + const aliasListisList = this.aliasColumnListSize < 0; + if (!aliasListisList) + throw new Error('Alias column list frame is not a list frame, which is required.'); + const aliasColumnCount = reader.readU32(); // number of alias column entries + console.log('Alias Column List Count:', aliasColumnCount); + const aliasColumns = []; + for (let i = 0; i < aliasColumnCount; ++i){ + const physicalColumnId = reader.readU32(), + fieldId = reader.readU32(); + aliasColumns.push({ + physicalColumnId, + fieldId + }); + } + this.aliasColumns = aliasColumns; +} +_readExtraTypeInformation(reader) { + this.extraTypeInfoListSize = reader.readS64(); // signed 64-bit + const isList = this.extraTypeInfoListSize < 0; + + if (!isList) + throw new Error('Extra type info frame is not a list frame, which is required.'); + + const entryCount = reader.readU32(); // number of extra type info entries + console.log('Extra Type Info Count:', entryCount); + + const extraTypeInfo = []; + for (let i = 0; i < entryCount; ++i) { + const contentId = reader.readU32(), + typeVersion = reader.readU32(); + extraTypeInfo.push({ + contentId, + typeVersion + }); + } + this.extraTypeInfo = extraTypeInfo; +} +_readClusterGroups(reader){ + const clusterGroupListSize = reader.readS64(), + isList = clusterGroupListSize < 0; + if (!isList) + throw new Error('Cluster group frame is not a list frame.'); + + const clusterGroupCount = reader.readU32(); + console.log('Cluster Group Count:', clusterGroupCount); + + const clusterGroups = []; + for (let i = 0; i < clusterGroupCount; ++i) { + const minEntry = reader.readS64(), + entrySpan = reader.readS64(), + numClusters = reader.readU32(); + clusterGroups.push({ + minEntry, + entrySpan, + numClusters + }); + } + this.clusterGroups = clusterGroups; +} } From 9d62f32094e24ee7ea30883facbd60337ed9c83a Mon Sep 17 00:00:00 2001 From: Krmjn09 Date: Sun, 15 Jun 2025 18:04:53 +0530 Subject: [PATCH 7/7] commit changes --- demo/node/rntuple.js | 25 +++++++++++++++++++ modules/rntuple.mjs | 57 +++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/demo/node/rntuple.js b/demo/node/rntuple.js index 5dc470ec2..3b19018f2 100644 --- a/demo/node/rntuple.js +++ b/demo/node/rntuple.js @@ -62,3 +62,28 @@ else else console.error('FAILURE: test 3 - readString does not match'); } +if (!rntuple.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]; + 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 (!rntuple.builder?.columnDescriptors?.length) + 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 col = rntuple.builder.columnDescriptors[i]; + if (typeof col.fieldId !== 'number') + console.error(`FAILURE: Column ${i} is missing a valid fieldId`); + else + console.log(`OK: Column ${i}: fieldId = ${col.fieldId}, type = ${col.coltype}`); + } +} diff --git a/modules/rntuple.mjs b/modules/rntuple.mjs index fb753b71d..1e6c16187 100644 --- a/modules/rntuple.mjs +++ b/modules/rntuple.mjs @@ -119,7 +119,13 @@ deserializeHeader(header_blob) { this._readFeatureFlags(reader); // Read metadata strings - this._readStrings(reader); + this.name = reader.readString(); + this.description = reader.readString(); + this.writer = reader.readString(); + // TODO: Remove debug logs before finalizing + console.log('Name:', this.name); + console.log('Description:', this.description); + console.log('Writer:', this.writer); // List frame: list of field record frames this._readFieldDescriptors(reader); @@ -178,18 +184,11 @@ _readFeatureFlags(reader) { if (this.featureFlags.some(v => v !== 0n)) throw new Error('Unexpected non-zero feature flags: ' + this.featureFlags); } -_readStrings(reader) { - this.name = reader.readString(); - this.description = reader.readString(); - this.writer = reader.readString(); - // TODO: Remove debug logs before finalizing - console.log('Name:', this.name); - console.log('Description:', this.description); - console.log('Writer:', this.writer); -} + _readFieldDescriptors(reader) { - this.fieldListSize = reader.readS64(); // signed 64-bit - const fieldListIsList = this.fieldListSize < 0; +const fieldListSize = reader.readS64(), // signed 64-bit +fieldListIsList = fieldListSize < 0; + if (!fieldListIsList) throw new Error('Field list frame is not a list frame, which is required.'); @@ -257,19 +256,27 @@ _readColumnDescriptors(reader) { maxValue = reader.readF64(); } - columnDescriptors.push({ - coltype, - bitsOnStrorage, - fieldId, - flags, - representationIndex, - firstElementIndex, - minValue, - maxValue, - isDeferred: (flags & 0x01) !== 0, - isSuppressed: (firstElementIndex !== null && firstElementIndex < 0) - }); - } + const column = { + coltype, + bitsOnStrorage, + fieldId, + flags, + representationIndex, + firstElementIndex, + minValue, + maxValue + }; + + column.isDeferred = function () { + return (this.flags & 0x01) !== 0; + }; + + column.isSuppressed = function () { + return this.firstElementIndex !== null && this.firstElementIndex < 0; + }; + + columnDescriptors.push(column); + } this.columnDescriptors = columnDescriptors; } _readAliasColumn(reader){