/** * @typedef {import('../src/types.js').FileMetaData} FileMetaData */ import { getColumnRange } from '../src/column.js' /** * @param {FileMetaData} metadata * @returns {HTMLDivElement} */ export function fileMetadata(metadata) { let html = '

Metadata

' html += `
${JSON.stringify(metadata, null, 2)}
` const div = document.createElement('div') div.innerHTML = html div.classList.add('layout', 'collapsed') // start collapsed div.children[0].addEventListener('click', () => { div.classList.toggle('collapsed') }) return div } /** * Render parquet file layout. * * @param {FileMetaData} metadata * @param {import('../src/types.js').AsyncBuffer} asyncBuffer * @returns {HTMLDivElement} */ export function fileLayout(metadata, asyncBuffer) { let html = '

File layout

' html += cell('PAR1', 0n, 4n) // magic number // data pages by row group and column /** @type {[string, bigint, bigint][]} */ const indexPages = [] for (const rowGroupIndex in metadata.row_groups) { const rowGroup = metadata.row_groups[rowGroupIndex] html += group(`RowGroup ${rowGroupIndex} (${rowGroup.total_byte_size.toLocaleString()} bytes)`) for (const column of rowGroup.columns) { const columnName = column.meta_data?.path_in_schema.join('.') html += group(`Column ${columnName}`) if (column.meta_data) { const end = getColumnRange(column.meta_data)[1] /* eslint-disable no-extra-parens */ const pages = (/** @type {[string, bigint][]} */ ([ ['Dictionary', column.meta_data.dictionary_page_offset], ['Data', column.meta_data.data_page_offset], ['Index', column.meta_data.index_page_offset], ['End', end], ])) .filter(([, offset]) => offset !== undefined) .sort((a, b) => Number(a[1]) - Number(b[1])) for (let i = 0; i < pages.length - 1; i++) { const [name, start] = pages[i] const end = pages[i + 1][1] html += cell(name, start, end) } } if (column.column_index_offset) { indexPages.push([`ColumnIndex RowGroup${rowGroupIndex} ${columnName}`, column.column_index_offset, BigInt(column.column_index_length || 0)]) } if (column.offset_index_offset) { indexPages.push([`OffsetIndex RowGroup${rowGroupIndex} ${columnName}`, column.offset_index_offset, BigInt(column.offset_index_length || 0)]) } html += '' } html += '' } // column and offset indexes for (const [name, start, length] of indexPages.sort((a, b) => Number(a[1]) - Number(b[1]))) { html += cell(name, start, start + length) } // metadata footer const metadataStart = BigInt(asyncBuffer.byteLength - metadata.metadata_length - 4) const metadataEnd = BigInt(asyncBuffer.byteLength - 4) html += cell('Metadata', metadataStart, metadataEnd) html += cell('PAR1', metadataEnd, BigInt(asyncBuffer.byteLength)) // magic number const div = document.createElement('div') div.innerHTML = html div.classList.add('layout', 'collapsed') // start collapsed div.children[0].addEventListener('click', () => { div.classList.toggle('collapsed') }) return div } /** * @param {string} name * @returns {string} */ function group(name) { return `
${name}` } /** * @param {string} name * @param {bigint} start * @param {bigint} end * @returns {string} */ function cell(name, start, end) { const bytes = end - start return `
` }