struffer
Version:
Struct + Buffer = Struffer
282 lines • 8.56 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const os_1 = require("os");
const util_1 = require("util");
const common_1 = require("./common");
class StrufferBase {
/*
* properties to be overridden by derrived classes.
* we ignore them in code coverage because they're
* never suppossed to be reached. derrived classes
* MUST override them.
*/
/* istanbul ignore next */
get kind() {
return 'null';
}
/* istanbul ignore next */
get info() {
return new Map();
}
/* istanbul ignore next */
get order() {
return [];
}
/* istanbul ignore next */
static get byteLength() {
return 0;
}
/* istanbul ignore next */
static get bitLength() {
return 0;
}
/* map-like properties */
get size() {
return this.order.length;
}
get [Symbol.toStringTag]() {
// `as 'Map'` to be compatible with Maps (in typescript's view)
return `${this.constructor.name}<${this.kind}>`;
}
constructor(buf, offset = 0) {
this.buffer = buf;
this.structure = new Proxy(Object.create(null), {
get: (self, _prop, recv) => {
const prop = String(_prop);
if (this.has(prop)) {
return this.get(prop);
}
return undefined;
},
set: (self, _prop, val, recv) => {
const prop = String(_prop);
if (this.has(prop)) {
this.set(prop, val);
return true;
}
return false;
},
has: (self, _prop) => {
const prop = String(_prop);
if (this.has(prop)) {
return true;
}
return false;
},
deleteProperty: (self, _prop) => {
const prop = String(_prop);
if (this.has(prop)) {
return this.delete(prop);
}
return false;
},
ownKeys: (self) => {
return [...this.keys()];
},
getOwnPropertyDescriptor: (self, _prop) => {
const prop = String(_prop);
if (this.has(prop)) {
return {
configurable: true,
enumerable: true,
writable: true,
};
}
return undefined;
},
});
this.offset = offset;
}
/*
* method stubs
* same reason for ignoring as the overriden properties
*/
/* istanbul ignore next */
getBits(name) {
return [];
}
/* istanbul ignore next */
setBits(name, bits) {
return;
}
/* map-like methods we care about */
get(name) {
const info = this.info.get(name);
if (!info) {
return undefined;
}
const bits = this.getBits(name);
/*
* this condition is hardware dependent, so it's nearly impossible
* to test properly for. let's tell istanbul to ignore it
*/
/* istanbul ignore next: hardware dependent check */
if (os_1.endianness() === 'LE') {
if (info.endianness === common_1.MemberEndianness.BigEndian) {
bits.reverse();
}
}
else /* (endianness() === 'BE') */ {
if (info.endianness === common_1.MemberEndianness.LittleEndian) {
bits.reverse();
}
}
if (info.signature === common_1.MemberSignature.Signed) {
return common_1.joinSignedBits(bits);
}
return common_1.joinBits(bits);
}
set(name, value) {
const info = this.info.get(name);
if (!info) {
throw new Error('Structure member not found!');
}
let bits = common_1.splitIntoBits(value, false);
// same reason as the endianness check in `get`
/* istanbul ignore next: hardware dependent check */
if (os_1.endianness() === 'LE') {
bits = bits.join('')
.padStart(info.bitSize, (value < 0) ? '1' : '0')
.split('')
.map(i => (i === '0') ? 0 : 1);
if (info.endianness === common_1.MemberEndianness.BigEndian) {
bits.reverse();
}
}
else /* (endianness() === 'BE') */ {
bits = bits.join('')
.padEnd(info.bitSize, (value < 0) ? '1' : '0')
.split('')
.map(i => (i === '0') ? 0 : 1);
if (info.endianness === common_1.MemberEndianness.LittleEndian) {
bits.reverse();
}
}
this.setBits(name, bits);
return this;
}
has(name) {
return this.info.has(name);
}
/* map-like methods we don't care about (i.e. only implemented for compatability) */
delete(name) {
if (this.has(name)) {
this.set(name, 0);
return true;
}
return false;
}
clear() {
this.deleteMany(this.order.slice());
}
forEach(cb, thisArg) {
for (const member of this.order) {
// override strict undefined check here, we know it exists
const value = this.get(member);
if (thisArg !== undefined) {
cb.call(thisArg, value, member, this);
}
else {
cb(value, member, this);
}
}
}
/* batch methods for convenience */
getMany(members = this.order.slice()) {
const dict = {};
for (const member of members) {
dict[member] = this.get(member);
}
return dict;
}
setMany(dictionary) {
for (const member of Object.keys(dictionary)) {
// override strict undefined check here, we know it exists
this.set(member, dictionary[member]);
}
}
hasMany(members) {
const table = {};
for (const member of members) {
table[member] = this.has(member);
}
return table;
}
deleteMany(members) {
let result = true;
for (const member of members) {
const tmp = this.delete(member);
if (result)
result = tmp;
}
return result;
}
/* iterators */
*keys() {
/*
* it would be easier to use `Array.prototype.values` here,
* but for some reason, Node doesn't have it!
*/
for (const member of this.order) {
yield member;
}
}
*values() {
for (const member of this.order) {
// override strict undefined check here, we know it exists
yield this.get(member);
}
}
*entries() {
for (const member of this.order) {
// same here
yield [member, this.get(member)];
}
}
[Symbol.iterator]() {
return this.entries();
}
*bits() {
for (const member of this.order) {
yield this.getBits(member);
}
}
*bitEntries() {
for (const member of this.order) {
yield [member, this.getBits(member)];
}
}
/* string representations */
toString() {
let str = `${this.constructor.name}<${this.kind}> {\n`;
for (const member of this.order) {
// override strict undefined check here, we know it exists
const info = this.info.get(member);
str += ' ';
str += member;
str += ': ';
str += (info.signature === common_1.MemberSignature.Signed) ? 'i' : 'u';
str += info.bitSize;
str += (info.endianness === common_1.MemberEndianness.BigEndian) ? 'be' : '';
str += ' = ';
str += this.get(member);
str += ';\n';
}
str += '}';
return str;
}
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
throw new TypeError(`Cannot convert ${this.constructor.name}<${this.kind}> to a Number`);
}
else {
return this.toString();
}
}
[util_1.inspect.custom](depth, opts) {
// TODO: stylize for node
return this.toString();
}
}
exports.default = StrufferBase;
//# sourceMappingURL=base.js.map