165 lines
3.6 KiB
JavaScript
165 lines
3.6 KiB
JavaScript
|
const encoder = new TextEncoder();
|
||
|
|
||
|
export default class Schema {
|
||
|
|
||
|
$$size = 0;
|
||
|
|
||
|
specification;
|
||
|
|
||
|
static viewGetMethods = {
|
||
|
uint8: 'getUint8',
|
||
|
int8: 'getInt8',
|
||
|
uint16: 'getUint16',
|
||
|
int16: 'getInt16',
|
||
|
uint32: 'getUint32',
|
||
|
int32: 'getInt32',
|
||
|
float32: 'getFloat32',
|
||
|
float64: 'getFloat64',
|
||
|
int64: 'getBigInt64',
|
||
|
uint64: 'getBigUint64',
|
||
|
};
|
||
|
|
||
|
static viewSetMethods = {
|
||
|
uint8: 'setUint8',
|
||
|
int8: 'setInt8',
|
||
|
uint16: 'setUint16',
|
||
|
int16: 'setInt16',
|
||
|
uint32: 'setUint32',
|
||
|
int32: 'setInt32',
|
||
|
float32: 'setFloat32',
|
||
|
float64: 'setFloat64',
|
||
|
int64: 'setBigInt64',
|
||
|
uint64: 'setBigUint64',
|
||
|
};
|
||
|
|
||
|
constructor(specification) {
|
||
|
this.specification = this.constructor.normalize(specification);
|
||
|
// Try to calculate static size.
|
||
|
for (const i in this.specification) {
|
||
|
const {type} = this.specification[i];
|
||
|
const size = this.constructor.sizeOfType(type);
|
||
|
if (0 === size) {
|
||
|
this.$$size = 0;
|
||
|
break;
|
||
|
}
|
||
|
this.$$size += size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Symbol.iterator]() {
|
||
|
return Object.entries(this.specification).values();
|
||
|
}
|
||
|
|
||
|
static defaultValueForType(type) {
|
||
|
switch (type) {
|
||
|
case 'uint8': case 'int8':
|
||
|
case 'uint16': case 'int16':
|
||
|
case 'uint32': case 'int32':
|
||
|
case 'uint64': case 'int64':
|
||
|
case 'float32': case 'float64': {
|
||
|
return 0;
|
||
|
}
|
||
|
case 'string': {
|
||
|
return '';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
has(key) {
|
||
|
return key in this.specification;
|
||
|
}
|
||
|
|
||
|
static normalize(specification) {
|
||
|
const normalized = Object.create(null);
|
||
|
for (const i in specification) {
|
||
|
normalized[i] = 'string' === typeof specification[i]
|
||
|
? {type: specification[i]}
|
||
|
: specification[i];
|
||
|
if (!this.validateType(normalized[i].type)) {
|
||
|
throw new TypeError(`unknown schema type: ${normalized[i].type}`);
|
||
|
}
|
||
|
normalized[i].defaultValue = normalized[i].defaultValue || this.defaultValueForType(normalized[i].type);
|
||
|
}
|
||
|
return normalized;
|
||
|
}
|
||
|
|
||
|
readSize(view, cursor) {
|
||
|
let fullSize = 0;
|
||
|
for (const i in this.specification) {
|
||
|
const {type} = this.specification[i];
|
||
|
const size = this.constructor.sizeOfType(type);
|
||
|
if (0 === size) {
|
||
|
switch (type) {
|
||
|
case 'string': {
|
||
|
const length = view.getUint32(cursor, true);
|
||
|
cursor += 4 + length;
|
||
|
fullSize += 4;
|
||
|
fullSize += length;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
cursor += size;
|
||
|
fullSize += size;
|
||
|
}
|
||
|
}
|
||
|
return fullSize;
|
||
|
}
|
||
|
|
||
|
get size() {
|
||
|
return this.$$size;
|
||
|
}
|
||
|
|
||
|
sizeOf(concrete) {
|
||
|
let fullSize = 0;
|
||
|
for (const i in this.specification) {
|
||
|
const {type} = this.specification[i];
|
||
|
const size = this.constructor.sizeOfType(type);
|
||
|
if (0 === size) {
|
||
|
switch (type) {
|
||
|
case 'string':
|
||
|
fullSize += 4;
|
||
|
fullSize += (encoder.encode(concrete[i])).length;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
fullSize += size;
|
||
|
}
|
||
|
}
|
||
|
return fullSize;
|
||
|
}
|
||
|
|
||
|
static sizeOfType(type) {
|
||
|
switch (type) {
|
||
|
case 'uint8': case 'int8': {
|
||
|
return 1;
|
||
|
}
|
||
|
case 'uint16': case 'int16': {
|
||
|
return 2;
|
||
|
}
|
||
|
case 'uint32': case 'int32': case 'float32': {
|
||
|
return 4;
|
||
|
}
|
||
|
case 'uint64': case 'int64': case 'float64': {
|
||
|
return 8;
|
||
|
}
|
||
|
default: return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static validateType(type) {
|
||
|
return [
|
||
|
'uint8', 'int8',
|
||
|
'uint16', 'int16',
|
||
|
'uint32', 'int32',
|
||
|
'uint64', 'int64',
|
||
|
'float32', 'float64',
|
||
|
'string',
|
||
|
]
|
||
|
.includes(type);
|
||
|
}
|
||
|
|
||
|
}
|