UNPKG

scran.js

Version:

Single cell RNA-seq analysis in Javascript

378 lines (337 loc) 9.5 kB
import * as utils from "./utils.js"; import * as wasm from "./wasm.js"; import * as gc from "./gc.js"; /** * Base class for RDS objects. * @hideconstructor */ export class RdsObject { constructor(id, raw, par) { this.id = id; this.object = raw; this.parent = par; } /** * @return {string} Type of the object. */ type() { return this.object.type(); } /** * Free the memory on the Wasm heap for this object. */ free() { if (this.object !== null) { gc.release(this.id); this.object = null; } } } /** * Vector-like R object. * * @augments RdsObject * @hideconstructor */ export class RdsVector extends RdsObject { constructor(id, raw, par) { super(id, raw, par); } /** * @return{number} Length of the vector. */ length() { return this.object.size(); } /** * @return {Array} Names of all attributes. */ attributeNames() { return wasm.call(mod => this.object.attribute_names()); } /** * @param {string} name - Name of the attribute of interest. * @return {number} Index of `name` in the array of attributes from {@linkcode RdsVector#attributeNames attributeNames}. * If `name` is not present, -1 is returned. */ findAttribute(name) { return wasm.call(mod => this.object.find_attribute(name)); } /** * @param {number|string} i - Index or name of the attribute of interest. * @return {RdsObject} Value of the attribute. */ attribute(i) { if (typeof i == "number") { return dispatch(mod => this.object.load_attribute_by_index(i), this.parent); } else { return dispatch(mod => this.object.load_attribute_by_name(i), this.parent); } } } /** * Integer vector from R. * * @augments RdsVector * @hideconstructor */ export class RdsIntegerVector extends RdsVector { constructor(id, raw, par) { super(id, raw, par); } /** * @param {object} [options={}] - Optional parameters. * @param {boolean|string} [options.copy=true] - Whether to copy the results from the Wasm heap, see {@linkcode possibleCopy}. * * @return {Int32Array|Int32WasmArray} Values of the integer vector. */ values(options = {}) { const { copy = true, ...others } = options; utils.checkOtherOptions(others); return utils.possibleCopy(this.object.numeric_vector(), copy); } } /** * Boolean (i.e., boolean) vector from R. * * @augments RdsVector * @hideconstructor */ export class RdsBooleanVector extends RdsVector { constructor(id, raw, par) { super(id, raw, par); } /** * @param {object} [options={}] - Optional parameters. * @param {boolean|string} [options.copy=true] - Whether to copy the results from the Wasm heap, see {@linkcode possibleCopy}. * * @return {Int32Array|Int32WasmArray} Values of the logical vector. * Zero values are falsey and values of 1 are truthy. */ values(options = {}) { const { copy = true, ...others } = options; utils.checkOtherOptions(others); return utils.possibleCopy(this.object.numeric_vector(), copy); } } /** * Double-precision vector from R. * * @augments RdsVector * @hideconstructor */ export class RdsDoubleVector extends RdsVector { constructor(id, raw, par) { super(id, raw, par); } /** * @param {object} [options={}] - Optional parameters. * @param {boolean|string} [options.copy=true] - Whether to copy the results from the Wasm heap, see {@linkcode possibleCopy}. * * @return {Float64Array|Float64WasmArray} Values of the double vector. */ values(options = {}) { const { copy = true, ...others } = options; utils.checkOtherOptions(others); return utils.possibleCopy(this.object.numeric_vector(), copy); } } /** * String vector from R. * * @augments RdsVector * @hideconstructor */ export class RdsStringVector extends RdsVector { constructor(id, raw, par) { super(id, raw, par); } /** * @return {Array} Values of the string vector. */ values() { return wasm.call(mod => this.object.string_vector()); } } /** * Generic vector from R, typically known as a "list". * * @augments RdsVector * @hideconstructor */ export class RdsGenericVector extends RdsVector { constructor(id, raw, par) { super(id, raw, par); } /** * @param {number} index - Index of the list element of interest. * @return {RdsObject} Value of the list element. */ load(index) { return dispatch(mod => this.object.load_list_element(index), this.parent); } } /** * S4 object from R, containing slot data in its attributes. * * @augments RdsObject * @hideconstructor */ export class RdsS4Object extends RdsObject { constructor(id, raw, par) { super(id, raw, par); } /** * Name of the R class. */ className() { return wasm.call(mod => this.object.class_name()); } /** * Name of the package that defines the class. */ packageName() { return wasm.call(mod => this.object.package_name()); } /** * @return {Array} Names of all attributes. */ attributeNames() { return wasm.call(mod => this.object.attribute_names()); } /** * @param {string} name - Name of the attribute of interest. * @return {number} Index of `name` in the array of attributes from {@linkcode RdsVector#attributeNames attributeNames}. * If `name` is not present, -1 is returned. */ findAttribute(name) { return wasm.call(mod => this.object.find_attribute(name)); } /** * @param {number|string} i - Index or name of the attribute of interest. * @return {RdsObject} Value of the attribute. */ attribute(i) { if (typeof i == "number") { return dispatch(mod => this.object.load_attribute_by_index(i), this.parent); } else { return dispatch(mod => this.object.load_attribute_by_name(i), this.parent); } } } /** * NULL type in R. * * @augments RdsObject * @hideconstructor */ export class RdsNull extends RdsVector { constructor(id, raw, par) { super(id, raw, par); } }; function dispatch(fun, par) { let obj = wasm.call(fun); let tt = null; try { tt = obj.type(); } catch (e) { obj.delete(); throw e; } // Remaining steps until gc.call() should be no-throw! let cons; if (tt == "integer") { cons = RdsIntegerVector; } else if (tt == "double") { cons = RdsDoubleVector; } else if (tt == "boolean") { cons = RdsBooleanVector; } else if (tt == "string") { cons = RdsStringVector; } else if (tt == "vector") { cons = RdsGenericVector; } else if (tt == "S4") { cons = RdsS4Object; } else if (tt == "null") { cons = RdsNull; } else { cons = RdsObject; } return gc.call(mod => obj, cons, par); } /** * Details of the RDS file. * @hideconstructor */ export class RdsDetails { #id; #obj; constructor(id, obj) { this.#id = id; this.#obj = obj; } /** * @return {number} Version of the RDS format. This should be 3. */ formatVersion() { return this.#obj.format_version(); } /** * @return {string} The R version used to create the file. */ writerVersion() { let info = this.#obj.writer_version(); return String(info[0]) + "." + String(info[1]) + "." + String(info[2]); } /** * @return {string} The minimum R version that can read the file. */ readerVersion() { let info = this.#obj.reader_version(); return String(info[0]) + "." + String(info[1]) + "." + String(info[2]); } /** * @return {RdsObject} Interface into the underlying R object. */ value() { return dispatch(mod => this.#obj.load(), this); } /** * Free the memory on the Wasm heap for this object. * Doing so will invalidate all {@linkplain RdsObject} instances derived from this object, * directly via {@linkcode RdsDetails#load} or indirectly * (e.g., from further {@linkcode RdsVector#attribute RdsVector.attribute} or {@linkcode RdsGenericVector#load RdsGenericVector.load} calls). */ free() { if (this.#obj !== null) { gc.release(this.#id); this.#obj = null; } } } /** * Read the contents of an RDS file. * * @param {Uint8WasmArray|Array|TypedArray|string} buffer Byte array containing the contents of an RDS file. * This can be raw text or Gzip-compressed. * * Alternatively, this can be a string containing a file path to a MatrixMarket file. * * @return {RdsDetails} Details of the file. */ export function readRds(x) { let tmp; let output; try { if (typeof x == "string") { output = gc.call(module => module.parse_rds_from_file(x), RdsDetails) } else { tmp = utils.wasmifyArray(x, "Uint8WasmArray"); output = gc.call(module => module.parse_rds_from_buffer(tmp.offset, tmp.length), RdsDetails); } } finally { utils.free(tmp); } return output; }