// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ Copyright (c) 2017, the Perspective Authors. ┃ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ // ┃ This file is part of the Perspective library, distributed under the terms ┃ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ import type { View } from "@perspective-dev/client"; export interface ColumnData { type: "float32" | "float64" | "int32" | "string"; values?: Float32Array | Float64Array | Int32Array; /** * Dictionary key indices for string columns. */ indices?: Int32Array; /** * Dictionary values for string columns. */ dictionary?: string[]; /** * Arrow validity bitfield (1 bit per row). */ valid?: Uint8Array; } export type ColumnDataMap = Map; export interface TypedArrayWindowOptions { start_row?: number; end_row?: number; start_col?: number; end_col?: number; float32?: boolean; } /** * Fetches all columns from a View using `with_typed_arrays` and * builds a `ColumnDataMap`. The `values` typed arrays and `valid` * bitmaps are zero-copy views into WASM memory and remain valid only * for the duration of the `render` callback — if `render` returns a * `Promise`, the underlying `with_typed_arrays` call awaits it before * releasing the backing Arrow buffer. Callers must not retain any * `ColumnData` reference past `render`'s resolution. */ export async function viewToColumnDataMap( view: View, render: (data: ColumnDataMap) => void | Promise, options?: TypedArrayWindowOptions, ): Promise { const result: ColumnDataMap = new Map(); await (view as any).with_typed_arrays( options ?? {}, async ( names: string[], values: ArrayLike[], validities: (Uint8Array | null)[], dictionaries: (string[] | null)[], ) => { for (let i = 0; i < names.length; i++) { const name = names[i]; const vals = values[i]; const valid = validities[i] ?? undefined; const dict = dictionaries[i]; if (dict !== null) { result.set(name, { type: "string", indices: vals as Int32Array, dictionary: dict, valid, }); } else if (vals instanceof Float32Array) { result.set(name, { type: "float32", values: vals, valid }); } else if (vals instanceof Int32Array) { result.set(name, { type: "int32", values: vals, valid }); } else if (vals instanceof Float64Array) { // Datetime/Date columns are emitted as Float64 to keep // millisecond precision; numeric Float64 also lands here // when `float32` mode is off. Keep them as f64 — the // chart's CPU mirrors and extents will rebase to f32 at // upload time. result.set(name, { type: "float64", values: vals, valid }); } else { // Fallback: treat as float32 // TODO: Instance check if this needs a copy? result.set(name, { type: "float32", values: new Float32Array(vals as any), valid, }); } } await render(result); }, ); }