ipdbot/node_modules/erlpack/js/decoder.h
2022-04-18 15:32:07 +02:00

458 lines
13 KiB
C++

#pragma once
#if !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS
#endif
#include <nan.h>
#include <zlib.h>
#include <cinttypes>
#include <cstdio>
#include "../cpp/sysdep.h"
using namespace v8;
#define THROW(msg) Nan::ThrowError(msg); isInvalid = true; printf("[Error %s:%d] %s\n", __FILE__, __LINE__, msg)
class Decoder {
public:
Decoder(const Nan::TypedArrayContents<uint8_t>& array)
: data(*array)
, size(array.length())
, isInvalid(false)
, offset(0)
{
const auto version = read8();
if (version != FORMAT_VERSION) {
THROW("Bad version number.");
isInvalid = true;
}
}
Decoder(const uint8_t* data_, size_t length_, bool skipVersion = false)
: data(data_)
, size(length_)
, isInvalid(false)
, offset(0)
{
if (!skipVersion) {
const auto version = read8();
if (version != FORMAT_VERSION) {
THROW("Bad version number.");
isInvalid = true;
}
}
}
uint8_t read8() {
if (offset + sizeof(uint8_t) > size) {
THROW("Reading a byte passes the end of the buffer.");
return 0;
}
auto val = *reinterpret_cast<const uint8_t*>(data + offset);
offset += sizeof(uint8_t);
return val;
}
uint16_t read16() {
if (offset + sizeof(uint16_t) > size) {
THROW("Reading two bytes passes the end of the buffer.");
return 0;
}
uint16_t val = _erlpack_be16(*reinterpret_cast<const uint16_t*>(data + offset));
offset += sizeof(uint16_t);
return val;
}
uint32_t read32() {
if (offset + sizeof(uint32_t) > size) {
THROW("Reading three bytes passes the end of the buffer.");
return 0;
}
uint32_t val = _erlpack_be32(*reinterpret_cast<const uint32_t*>(data + offset));
offset += sizeof(uint32_t);
return val;
}
uint64_t read64() {
if (offset + sizeof(uint64_t) > size) {
THROW("Reading four bytes passes the end of the buffer.");
return 0;
}
uint64_t val = _erlpack_be64(*reinterpret_cast<const uint64_t*>(data + offset));
offset += sizeof(val);
return val;
}
Local<Value> decodeSmallInteger() {
return Nan::New<Integer>(read8());
}
Local<Value> decodeInteger() {
return Nan::New<Integer>((int32_t)read32());
}
Local<Value> decodeArray(uint32_t length) {
Local<Object> array = Nan::New<Array>(length);
for(uint32_t i = 0; i < length; ++i) {
auto value = unpack();
if (isInvalid) {
return Nan::Undefined();
}
Nan::Set(array, i, value);
}
return array;
}
Local<Value> decodeList() {
const uint32_t length = read32();
auto array = decodeArray(length);
const auto tailMarker = read8();
if (tailMarker != NIL_EXT) {
THROW("List doesn't end with a tail marker, but it must!");
return Nan::Null();
}
return array;
}
Local<Value> decodeTuple(uint32_t length) {
return decodeArray(length);
}
Local<Value> decodeNil() {
Local<Object> array = Nan::New<Array>(0);
return array;
}
Local<Value> decodeMap() {
const uint32_t length = read32();
auto map = Nan::New<Object>();
for(uint32_t i = 0; i < length; ++i) {
const auto key = unpack();
const auto value = unpack();
if (isInvalid) {
return Nan::Undefined();
}
Nan::Set(map, key, value);
}
return map;
}
const char* readString(uint32_t length) {
if (offset + length > size) {
THROW("Reading sequence past the end of the buffer.");
return NULL;
}
const uint8_t* str = data + offset;
offset += length;
return (const char*)str;
}
Local<Value> processAtom(const char* atom, uint16_t length) {
if (atom == NULL) {
return Nan::Undefined();
}
if (length >= 3 && length <= 5) {
if (length == 3 && strncmp(atom, "nil", 3) == 0) {
return Nan::Null();
}
else if (length == 4 && strncmp(atom, "null", 4) == 0) {
return Nan::Null();
}
else if(length == 4 && strncmp(atom, "true", 4) == 0) {
return Nan::True();
}
else if (length == 5 && strncmp(atom, "false", 5) == 0) {
return Nan::False();
}
}
return Nan::New(atom, length).ToLocalChecked();
}
Local<Value> decodeAtom() {
auto length = read16();
const char* atom = readString(length);
return processAtom(atom, length);
}
Local<Value> decodeSmallAtom() {
auto length = read8();
const char* atom = readString(length);
return processAtom(atom, length);
}
Local<Value> decodeFloat() {
const uint8_t FLOAT_LENGTH = 31;
const char* floatStr = readString(FLOAT_LENGTH);
if (floatStr == NULL) {
return Nan::Undefined();
}
double number;
char nullTerimated[FLOAT_LENGTH + 1] = {0};
memcpy(nullTerimated, floatStr, FLOAT_LENGTH);
auto count = sscanf(nullTerimated, "%lf", &number);
if (count != 1) {
THROW("Invalid float encoded.");
return Nan::Null();
}
return Nan::New<Number>(number);
}
Local<Value> decodeNewFloat() {
union {
uint64_t ui64;
double df;
} val;
val.ui64 = read64();
return Nan::New<Number>(val.df);
}
Local<Value> decodeBig(uint32_t digits) {
const uint8_t sign = read8();
if (digits > 8) {
THROW("Unable to decode big ints larger than 8 bytes");
return Nan::Null();
}
uint64_t value = 0;
uint64_t b = 1;
for(uint32_t i = 0; i < digits; ++i) {
uint64_t digit = read8();
value += digit * b;
b <<= 8;
}
if (digits <= 4) {
if (sign == 0) {
return Nan::New<Integer>(static_cast<uint32_t>(value));
}
const bool isSignBitAvailable = (value & (1 << 31)) == 0;
if (isSignBitAvailable) {
int32_t negativeValue = -static_cast<int32_t>(value);
return Nan::New<Integer>(negativeValue);
}
}
char outBuffer[32] = {0}; // 9223372036854775807
const char* const formatString = sign == 0 ? "%" PRIu64 : "-%" PRIu64;
const int res = sprintf(outBuffer, formatString, value);
if (res < 0) {
THROW("Unable to convert big int to string");
return Nan::Null();
}
const uint8_t length = static_cast<const uint8_t>(res);
return Nan::New(outBuffer, length).ToLocalChecked();
}
Local<Value> decodeSmallBig() {
const auto bytes = read8();
return decodeBig(bytes);
}
Local<Value> decodeLargeBig() {
const auto bytes = read32();
return decodeBig(bytes);
}
Local<Value> decodeBinaryAsString() {
const auto length = read32();
const char* str = readString(length);
if (str == NULL) {
return Nan::Undefined();
}
auto binaryString = Nan::New(str, length);
return binaryString.ToLocalChecked();
}
Local<Value> decodeString() {
const auto length = read16();
const char* str = readString(length);
if (str == NULL) {
return Nan::Undefined();
}
auto binaryString = Nan::New(str, length);
return binaryString.ToLocalChecked();
}
Local<Value> decodeStringAsList() {
const auto length = read16();
if (offset + length > size) {
THROW("Reading sequence past the end of the buffer.");
return Nan::Null();
}
Local<Object> array = Nan::New<Array>(length);
for(uint16_t i = 0; i < length; ++i) {
Nan::Set(array, i, decodeSmallInteger());
}
return array;
}
Local<Value> decodeSmallTuple() {
return decodeTuple(read8());
}
Local<Value> decodeLargeTuple() {
return decodeTuple(read32());
}
Local<Value> decodeCompressed() {
const uint32_t uncompressedSize = read32();
unsigned long sourceSize = uncompressedSize;
uint8_t* outBuffer = (uint8_t*)malloc(uncompressedSize);
const int ret = uncompress(outBuffer, &sourceSize, (const unsigned char*)(data + offset), (uLong)(size - offset));
offset += sourceSize;
if (ret != Z_OK) {
free(outBuffer);
THROW("Failed to uncompresss compressed item");
return Nan::Null();
}
Decoder children(outBuffer, uncompressedSize, true);
Nan::MaybeLocal<Value> value = children.unpack();
free(outBuffer);
return value.ToLocalChecked();
}
Local<Value> decodeReference() {
auto reference = Nan::New<Object>();
Nan::Set(reference, Nan::New("node").ToLocalChecked(), unpack()).FromJust();
Local<Object> ids = Nan::New<Array>(1);
Nan::Set(ids, 0, Nan::New<Integer>(read32())).FromJust();
Nan::Set(reference, Nan::New("id").ToLocalChecked(), ids).FromJust();
Nan::Set(reference, Nan::New("creation").ToLocalChecked(), Nan::New<Integer>(read8())).FromJust();
return reference;
}
Local<Value> decodeNewReference() {
auto reference = Nan::New<Object>();
uint16_t len = read16();
Nan::Set(reference, Nan::New("node").ToLocalChecked(), unpack()).FromJust();
Nan::Set(reference, Nan::New("creation").ToLocalChecked(), Nan::New<Integer>(read8())).FromJust();
Local<Object> ids = Nan::New<Array>(len);
for(uint16_t i = 0; i < len; ++i) {
Nan::Set(ids, i, Nan::New<Integer>(read32())).FromJust();
}
Nan::Set(reference, Nan::New("id").ToLocalChecked(), ids).FromJust();
return reference;
}
Local<Value> decodePort() {
auto port = Nan::New<Object>();
Nan::Set(port, Nan::New("node").ToLocalChecked(), unpack()).FromJust();
Nan::Set(port, Nan::New("id").ToLocalChecked(), Nan::New<Integer>(read32())).FromJust();
Nan::Set(port, Nan::New("creation").ToLocalChecked(), Nan::New<Integer>(read8())).FromJust();
return port;
}
Local<Value> decodePID() {
auto pid = Nan::New<Object>();
Nan::Set(pid, Nan::New("node").ToLocalChecked(), unpack()).FromJust();
Nan::Set(pid, Nan::New("id").ToLocalChecked(), Nan::New<Integer>(read32())).FromJust();
Nan::Set(pid, Nan::New("serial").ToLocalChecked(), Nan::New<Integer>(read32())).FromJust();
Nan::Set(pid, Nan::New("creation").ToLocalChecked(), Nan::New<Integer>(read8())).FromJust();
return pid;
}
Local<Value> decodeExport() {
auto exp = Nan::New<Object>();
Nan::Set(exp, Nan::New("mod").ToLocalChecked(), unpack()).FromJust();
Nan::Set(exp, Nan::New("fun").ToLocalChecked(), unpack()).FromJust();
Nan::Set(exp, Nan::New("arity").ToLocalChecked(), unpack()).FromJust();
return exp;
}
Local<Value> unpack() {
if (isInvalid) {
return Nan::Undefined();
}
if(offset >= size) {
THROW("Unpacking beyond the end of the buffer");
return Nan::Undefined();
}
const auto type = read8();
switch(type) {
case SMALL_INTEGER_EXT:
return decodeSmallInteger();
case INTEGER_EXT:
return decodeInteger();
case FLOAT_EXT:
return decodeFloat();
case NEW_FLOAT_EXT:
return decodeNewFloat();
case ATOM_EXT:
return decodeAtom();
case SMALL_ATOM_EXT:
return decodeSmallAtom();
case SMALL_TUPLE_EXT:
return decodeSmallTuple();
case LARGE_TUPLE_EXT:
return decodeLargeTuple();
case NIL_EXT:
return decodeNil();
case STRING_EXT:
return decodeStringAsList();
case LIST_EXT:
return decodeList();
case MAP_EXT:
return decodeMap();
case BINARY_EXT:
return decodeBinaryAsString();
case SMALL_BIG_EXT:
return decodeSmallBig();
case LARGE_BIG_EXT:
return decodeLargeBig();
case REFERENCE_EXT:
return decodeReference();
case NEW_REFERENCE_EXT:
return decodeNewReference();
case PORT_EXT:
return decodePort();
case PID_EXT:
return decodePID();
case EXPORT_EXT:
return decodeExport();
case COMPRESSED:
return decodeCompressed();
default:
THROW("Unsupported erlang term type identifier found");
return Nan::Undefined();
}
return Nan::Undefined();
}
private:
const uint8_t* const data;
const size_t size;
bool isInvalid;
size_t offset;
};