UNPKG

parquets

Version:

TypeScript implementation of the Parquet file format, based on parquet.js

201 lines (177 loc) 5.01 kB
import fs = require('fs'); import { TBufferedTransport, TCompactProtocol, TFramedTransport } from 'thrift'; import { FileMetaData, PageHeader } from './thrift'; import { Writable } from 'stream'; export interface WriteStreamOptions { flags?: string; encoding?: string; fd?: number; mode?: number; autoClose?: boolean; start?: number; } class UFramedTransport extends TFramedTransport { public readPos: number; } /** * Helper function that serializes a thrift object into a buffer */ export function serializeThrift(obj: any): Buffer { const output: Buffer[] = []; const transport = new TBufferedTransport(null, (buf) => { output.push(buf); }); const protocol = new TCompactProtocol(transport); obj.write(protocol); transport.flush(); return Buffer.concat(output); } export function decodeThrift(obj: any, buf: Buffer, offset?: number) { if (!offset) { // tslint:disable-next-line:no-parameter-reassignment offset = 0; } const transport = new UFramedTransport(buf); transport.readPos = offset; const protocol = new TCompactProtocol(transport); obj.read(protocol); return transport.readPos - offset; } export function decodeFileMetadata(buf: Buffer, offset?: number) { if (!offset) { // tslint:disable-next-line:no-parameter-reassignment offset = 0; } const transport = new UFramedTransport(buf); transport.readPos = offset; const protocol = new TCompactProtocol(transport); const metadata = FileMetaData.read(protocol); return { length: transport.readPos - offset, metadata }; } export function decodePageHeader(buf: Buffer, offset?: number) { if (!offset) { // tslint:disable-next-line:no-parameter-reassignment offset = 0; } const transport = new UFramedTransport(buf); transport.readPos = offset; const protocol = new TCompactProtocol(transport); const pageHeader = PageHeader.read(protocol); return { length: transport.readPos - offset, pageHeader }; } /** * Get the number of bits required to store a given value */ export function getBitWidth(val: number): number { if (val === 0) { return 0; // tslint:disable-next-line:no-else-after-return } else { return Math.ceil(Math.log2(val + 1)); } } /** * FIXME not ideal that this is linear */ export function getThriftEnum(klass: any, value: number | string): string { for (const k in klass) { if (klass[k] === value) { return k; } } throw new Error('Invalid ENUM value'); } export function fopen(filePath: string): Promise<number> { return new Promise((resolve, reject) => { fs.open(filePath, 'r', (err, fd) => { if (err) { reject(err); } else { resolve(fd); } }); }); } export function fstat(filePath: string): Promise<fs.Stats> { return new Promise((resolve, reject) => { fs.stat(filePath, (err, stat) => { if (err) { reject(err); } else { resolve(stat); } }); }); } export function fread(fd: number, position: number, length: number): Promise<Buffer> { const buffer = Buffer.alloc(length); return new Promise((resolve, reject) => { fs.read(fd, buffer, 0, length, position, (err, bytesRead, buf) => { if (err || bytesRead !== length) { reject(err || Error('read failed')); } else { resolve(buf); } }); }); } export function fclose(fd: number): Promise<void> { return new Promise((resolve, reject) => { fs.close(fd, (err) => { if (err) { reject(err); } else { resolve(); } }); }); } export function oswrite(os: Writable, buf: Buffer): Promise<void> { return new Promise((resolve, reject) => { os.write(buf, (err) => { if (err) { reject(err); } else { resolve(); } }); }); } export function osclose(os: Writable): Promise<void> { return new Promise((resolve, reject) => { (os as any).close((err: any) => { if (err) { reject(err); } else { resolve(); } }); }); } export function osopen(path: string, opts: WriteStreamOptions): Promise<fs.WriteStream> { return new Promise((resolve, reject) => { const outputStream = fs.createWriteStream(path, opts); outputStream.once('open', fd => resolve(outputStream)); outputStream.once('error', err => reject(err)); }); } // Supports MQTT path wildcards // + all immediate children // # all descendents export function fieldIndexOf(arr: string[][], elem: string[]): number { for (let j = 0; j < arr.length; j++) { if (arr[j].length > elem.length) continue; let m = true; for (let i = 0; i < elem.length; i++) { if (arr[j][i] === elem[i] || arr[j][i] === '+' || arr[j][i] === '#') continue; if (i >= arr[j].length && arr[j][arr[j].length - 1] === '#') continue; m = false; break; } if (m) return j; } return -1; } export function load(name: string): any { return (module || global as any)['require'](name); }