UNPKG

@xogeny/mat-parser

Version:

A parser for MATLAB v4 files

170 lines 7.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const types_1 = require("./types"); const debug = require("debug"); const parserDebug = debug("mat-parser:parser"); var Expecting; (function (Expecting) { Expecting[Expecting["Header"] = 0] = "Header"; Expecting[Expecting["Name"] = 1] = "Name"; Expecting[Expecting["Row"] = 2] = "Row"; Expecting[Expecting["Nothing"] = 3] = "Nothing"; })(Expecting || (Expecting = {})); /** * This class takes data from an Observer that passes a sequence of buffer corresponding to the * bytes in a MATLAB v4 file. This class parses that binary data and fires calls to a Handler * class that can then do with that information what it wishes. * * The format is documented here: * * https://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf */ class MatFile { constructor(source) { this.source = source; this.resetState(); } resetState() { this.state = { rem: Buffer.alloc(0), expecting: Expecting.Header, header: null, colnum: null, name: null, }; } parse(handler) { this.resetState(); return new Promise((resolve, reject) => { let sub = this.source.subscribe((chunk) => { this.state.rem = Buffer.concat([this.state.rem, chunk]); try { while (this.processBuffer(handler)) ; } catch (e) { console.error("Error processing buffer: ", e); sub.unsubscribe(); reject(e); } }, (err) => { reject(err); }, () => { if (this.state.rem.length == 0) { handler.eof(); resolve(undefined); } else reject(new Error("Ran out of data prematurely")); }); }); } /** * This method returns false if it cannot process anything else. */ processBuffer(handler) { let size = this.state.rem.length; switch (this.state.expecting) { case Expecting.Header: if (size < 20) return false; let type = this.state.rem.readInt32LE(0); let rows = this.state.rem.readInt32LE(4); let cols = this.state.rem.readInt32LE(8); let imaginary = this.state.rem.readInt32BE(12); let namelen = this.state.rem.readInt32LE(16); let dec = type.toString(); while (dec.length < 4) { dec = "0" + dec; } let M = dec[0]; let O = dec[1]; let P = dec[2]; let T = dec[3]; if (M != "0") { throw new Error("Only big-endian architectures are currently supported"); } if (O != "0") { throw new Error("Expected O to be zero, but it was " + O); } this.state.header = { data: types_1.dataType(P), matrix: types_1.matrixType(T), rows: rows, cols: cols, imaginary: imaginary > 0, namelen: namelen, }; this.state.rem = this.state.rem.slice(20); this.state.expecting = Expecting.Name; return true; case Expecting.Name: if (this.state.header == null) { throw new Error("Reading name, but no header found...this should not happen"); } if (size < this.state.header.namelen) return false; let name = this.state.rem.slice(0, this.state.header.namelen - 1).toString("ascii"); parserDebug("Header for '%s' = %o", name, this.state.header); try { if (handler) { handler.start(name, this.state.header.rows, this.state.header.cols); } } catch (e) { console.error("Ignoring error in handling matrix event: ", e); } this.state.rem = this.state.rem.slice(this.state.header.namelen); this.state.colnum = 0; this.state.name = name; this.state.expecting = Expecting.Row; return true; case Expecting.Row: if (this.state.header == null) throw new Error("Reading name, but no header found. This should not happen"); if (this.state.colnum == null) throw new Error("Expected column number to be set, but was null. This should not happen"); if (this.state.name == null) throw new Error("Expected matrix to be named, but found null. This should not happen"); if (size < types_1.dataSize(this.state.header.data, this.state.header.rows)) return false; let col = []; for (let i = 0; i < this.state.header.rows; i++) { this.state.rem = types_1.readOne(col, this.state.header.data, this.state.rem); } try { if (handler) { handler.column(this.state.name, this.state.colnum, this.state.header.matrix, col, this.state.colnum == this.state.header.cols - 1); } } catch (e) { console.error("Ignore error in handling column event: ", e); } this.state.colnum++; if (this.state.colnum == this.state.header.cols) { let stop = false; if (handler) { stop = handler.end(this.state.name); } this.state.header = null; this.state.colnum = null; this.state.name = null; if (stop) { this.state.expecting = Expecting.Nothing; } else { this.state.expecting = Expecting.Header; } } return true; case Expecting.Nothing: this.state.name = null; this.state.rem = new Buffer([]); return false; default: throw new Error("Unexpected state: " + this.state.expecting); } } } exports.MatFile = MatFile; //# sourceMappingURL=parser.js.map