import * as utils from "./utils.js";
import * as wasm from "./wasm.js";
import * as gc from "./gc.js";
import * as packer from "./internal/pack_strings.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.fill_attribute_names();
let anames_buf = this.object.attribute_names_buffer();
let anames_len = this.object.attribute_names_length();
return packer.unpack_strings(anames_buf, anames_len);
});
}
/**
* @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.fill_string_vector();
let buf = this.object.string_vector_buffer();
let len = this.object.string_vector_length();
return packer.unpack_strings(buf, len);
});
}
}
/**
* 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.fill_attribute_names();
let anames_buf = this.object.attribute_names_buffer();
let anames_len = this.object.attribute_names_length();
return packer.unpack_strings(anames_buf, anames_len);
});
}
/**
* @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;
}