hap-nodejs
Version:
HAP-NodeJS is a Node.js implementation of HomeKit Accessory Server.
879 lines • 33.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataStreamWriter = exports.DataStreamReader = exports.DataStreamParser = exports.DataFormatTags = exports.UUID = exports.SecondsSince2001 = exports.Float64 = exports.Float32 = exports.Int64 = exports.Int32 = exports.Int16 = exports.Int8 = exports.ValueWrapper = void 0;
const tslib_1 = require("tslib");
const uuid = tslib_1.__importStar(require("../util/uuid"));
const hapCrypto = tslib_1.__importStar(require("../util/hapCrypto"));
const assert_1 = tslib_1.__importDefault(require("assert"));
const debug_1 = tslib_1.__importDefault(require("debug"));
// welcome to hell :)
// in this file lies madness and frustration. and It's not only about HDS. Also, JavaScript is hell
const debug = (0, debug_1.default)("HAP-NodeJS:DataStream:Parser");
class Magics {
static TERMINATOR = { type: "terminator" };
}
/**
* @group HomeKit Data Streams (HDS)
*/
class ValueWrapper {
value;
constructor(value) {
this.value = value;
}
equals(obj) {
return this.constructor.name === obj.constructor.name && obj.value === this.value;
}
}
exports.ValueWrapper = ValueWrapper;
/**
* @group HomeKit Data Streams (HDS)
*/
class Int8 extends ValueWrapper {
}
exports.Int8 = Int8;
/**
* @group HomeKit Data Streams (HDS)
*/
class Int16 extends ValueWrapper {
}
exports.Int16 = Int16;
/**
* @group HomeKit Data Streams (HDS)
*/
class Int32 extends ValueWrapper {
}
exports.Int32 = Int32;
/**
* @group HomeKit Data Streams (HDS)
*/
class Int64 extends ValueWrapper {
}
exports.Int64 = Int64;
/**
* @group HomeKit Data Streams (HDS)
*/
class Float32 extends ValueWrapper {
}
exports.Float32 = Float32;
/**
* @group HomeKit Data Streams (HDS)
*/
class Float64 extends ValueWrapper {
}
exports.Float64 = Float64;
/**
* @group HomeKit Data Streams (HDS)
*/
class SecondsSince2001 extends ValueWrapper {
}
exports.SecondsSince2001 = SecondsSince2001;
/**
* @group HomeKit Data Streams (HDS)
*/
class UUID extends ValueWrapper {
constructor(value) {
(0, assert_1.default)(uuid.isValid(value), "invalid uuid format");
super(value);
}
}
exports.UUID = UUID;
/**
* @group HomeKit Data Streams (HDS)
*/
var DataFormatTags;
(function (DataFormatTags) {
DataFormatTags[DataFormatTags["INVALID"] = 0] = "INVALID";
DataFormatTags[DataFormatTags["TRUE"] = 1] = "TRUE";
DataFormatTags[DataFormatTags["FALSE"] = 2] = "FALSE";
DataFormatTags[DataFormatTags["TERMINATOR"] = 3] = "TERMINATOR";
DataFormatTags[DataFormatTags["NULL"] = 4] = "NULL";
DataFormatTags[DataFormatTags["UUID"] = 5] = "UUID";
DataFormatTags[DataFormatTags["DATE"] = 6] = "DATE";
DataFormatTags[DataFormatTags["INTEGER_MINUS_ONE"] = 7] = "INTEGER_MINUS_ONE";
DataFormatTags[DataFormatTags["INTEGER_RANGE_START_0"] = 8] = "INTEGER_RANGE_START_0";
DataFormatTags[DataFormatTags["INTEGER_RANGE_STOP_39"] = 46] = "INTEGER_RANGE_STOP_39";
DataFormatTags[DataFormatTags["INT8"] = 48] = "INT8";
DataFormatTags[DataFormatTags["INT16LE"] = 49] = "INT16LE";
DataFormatTags[DataFormatTags["INT32LE"] = 50] = "INT32LE";
DataFormatTags[DataFormatTags["INT64LE"] = 51] = "INT64LE";
DataFormatTags[DataFormatTags["FLOAT32LE"] = 53] = "FLOAT32LE";
DataFormatTags[DataFormatTags["FLOAT64LE"] = 54] = "FLOAT64LE";
DataFormatTags[DataFormatTags["UTF8_LENGTH_START"] = 64] = "UTF8_LENGTH_START";
DataFormatTags[DataFormatTags["UTF8_LENGTH_STOP"] = 96] = "UTF8_LENGTH_STOP";
DataFormatTags[DataFormatTags["UTF8_LENGTH8"] = 97] = "UTF8_LENGTH8";
DataFormatTags[DataFormatTags["UTF8_LENGTH16LE"] = 98] = "UTF8_LENGTH16LE";
DataFormatTags[DataFormatTags["UTF8_LENGTH32LE"] = 99] = "UTF8_LENGTH32LE";
DataFormatTags[DataFormatTags["UTF8_LENGTH64LE"] = 100] = "UTF8_LENGTH64LE";
DataFormatTags[DataFormatTags["UTF8_NULL_TERMINATED"] = 111] = "UTF8_NULL_TERMINATED";
DataFormatTags[DataFormatTags["DATA_LENGTH_START"] = 112] = "DATA_LENGTH_START";
DataFormatTags[DataFormatTags["DATA_LENGTH_STOP"] = 144] = "DATA_LENGTH_STOP";
DataFormatTags[DataFormatTags["DATA_LENGTH8"] = 145] = "DATA_LENGTH8";
DataFormatTags[DataFormatTags["DATA_LENGTH16LE"] = 146] = "DATA_LENGTH16LE";
DataFormatTags[DataFormatTags["DATA_LENGTH32LE"] = 147] = "DATA_LENGTH32LE";
DataFormatTags[DataFormatTags["DATA_LENGTH64LE"] = 148] = "DATA_LENGTH64LE";
DataFormatTags[DataFormatTags["DATA_TERMINATED"] = 159] = "DATA_TERMINATED";
DataFormatTags[DataFormatTags["COMPRESSION_START"] = 160] = "COMPRESSION_START";
DataFormatTags[DataFormatTags["COMPRESSION_STOP"] = 207] = "COMPRESSION_STOP";
DataFormatTags[DataFormatTags["ARRAY_LENGTH_START"] = 208] = "ARRAY_LENGTH_START";
DataFormatTags[DataFormatTags["ARRAY_LENGTH_STOP"] = 222] = "ARRAY_LENGTH_STOP";
DataFormatTags[DataFormatTags["ARRAY_TERMINATED"] = 223] = "ARRAY_TERMINATED";
DataFormatTags[DataFormatTags["DICTIONARY_LENGTH_START"] = 224] = "DICTIONARY_LENGTH_START";
DataFormatTags[DataFormatTags["DICTIONARY_LENGTH_STOP"] = 238] = "DICTIONARY_LENGTH_STOP";
DataFormatTags[DataFormatTags["DICTIONARY_TERMINATED"] = 239] = "DICTIONARY_TERMINATED";
})(DataFormatTags || (exports.DataFormatTags = DataFormatTags = {}));
/**
* @group HomeKit Data Streams (HDS)
*/
class DataStreamParser {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static decode(buffer) {
const tag = buffer.readTag();
if (tag === 0 /* DataFormatTags.INVALID */) {
throw new Error("HDSDecoder: zero tag detected on index " + buffer.readerIndex);
}
else if (tag === 1 /* DataFormatTags.TRUE */) {
return buffer.readTrue();
}
else if (tag === 2 /* DataFormatTags.FALSE */) {
return buffer.readFalse();
}
else if (tag === 3 /* DataFormatTags.TERMINATOR */) {
return Magics.TERMINATOR;
}
else if (tag === 4 /* DataFormatTags.NULL */) {
return null;
}
else if (tag === 5 /* DataFormatTags.UUID */) {
return buffer.readUUID();
}
else if (tag === 6 /* DataFormatTags.DATE */) {
return buffer.readSecondsSince2001_01_01();
}
else if (tag === 7 /* DataFormatTags.INTEGER_MINUS_ONE */) {
return buffer.readNegOne();
}
else if (tag >= 8 /* DataFormatTags.INTEGER_RANGE_START_0 */ && tag <= 46 /* DataFormatTags.INTEGER_RANGE_STOP_39 */) {
return buffer.readIntRange(tag); // integer values from 0-39
}
else if (tag === 48 /* DataFormatTags.INT8 */) {
return buffer.readInt8();
}
else if (tag === 49 /* DataFormatTags.INT16LE */) {
return buffer.readInt16LE();
}
else if (tag === 50 /* DataFormatTags.INT32LE */) {
return buffer.readInt32LE();
}
else if (tag === 51 /* DataFormatTags.INT64LE */) {
return buffer.readInt64LE();
}
else if (tag === 53 /* DataFormatTags.FLOAT32LE */) {
return buffer.readFloat32LE();
}
else if (tag === 54 /* DataFormatTags.FLOAT64LE */) {
return buffer.readFloat64LE();
}
else if (tag >= 64 /* DataFormatTags.UTF8_LENGTH_START */ && tag <= 96 /* DataFormatTags.UTF8_LENGTH_STOP */) {
const length = tag - 64 /* DataFormatTags.UTF8_LENGTH_START */;
return buffer.readUTF8(length);
}
else if (tag === 97 /* DataFormatTags.UTF8_LENGTH8 */) {
return buffer.readUTF8_Length8();
}
else if (tag === 98 /* DataFormatTags.UTF8_LENGTH16LE */) {
return buffer.readUTF8_Length16LE();
}
else if (tag === 99 /* DataFormatTags.UTF8_LENGTH32LE */) {
return buffer.readUTF8_Length32LE();
}
else if (tag === 100 /* DataFormatTags.UTF8_LENGTH64LE */) {
return buffer.readUTF8_Length64LE();
}
else if (tag === 111 /* DataFormatTags.UTF8_NULL_TERMINATED */) {
return buffer.readUTF8_NULL_terminated();
}
else if (tag >= 112 /* DataFormatTags.DATA_LENGTH_START */ && tag <= 144 /* DataFormatTags.DATA_LENGTH_STOP */) {
const length = tag - 112 /* DataFormatTags.DATA_LENGTH_START */;
buffer.readData(length);
}
else if (tag === 145 /* DataFormatTags.DATA_LENGTH8 */) {
return buffer.readData_Length8();
}
else if (tag === 146 /* DataFormatTags.DATA_LENGTH16LE */) {
return buffer.readData_Length16LE();
}
else if (tag === 147 /* DataFormatTags.DATA_LENGTH32LE */) {
return buffer.readData_Length32LE();
}
else if (tag === 148 /* DataFormatTags.DATA_LENGTH64LE */) {
return buffer.readData_Length64LE();
}
else if (tag === 159 /* DataFormatTags.DATA_TERMINATED */) {
return buffer.readData_terminated();
}
else if (tag >= 160 /* DataFormatTags.COMPRESSION_START */ && tag <= 207 /* DataFormatTags.COMPRESSION_STOP */) {
const index = tag - 160 /* DataFormatTags.COMPRESSION_START */;
return buffer.decompressData(index);
}
else if (tag >= 208 /* DataFormatTags.ARRAY_LENGTH_START */ && tag <= 222 /* DataFormatTags.ARRAY_LENGTH_STOP */) {
const length = tag - 208 /* DataFormatTags.ARRAY_LENGTH_START */;
const array = [];
for (let i = 0; i < length; i++) {
array.push(this.decode(buffer));
}
return array;
}
else if (tag === 223 /* DataFormatTags.ARRAY_TERMINATED */) {
const array = [];
let element;
while ((element = this.decode(buffer)) !== Magics.TERMINATOR) {
array.push(element);
}
return array;
}
else if (tag >= 224 /* DataFormatTags.DICTIONARY_LENGTH_START */ && tag <= 238 /* DataFormatTags.DICTIONARY_LENGTH_STOP */) {
const length = tag - 224 /* DataFormatTags.DICTIONARY_LENGTH_START */;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dictionary = {};
for (let i = 0; i < length; i++) {
const key = this.decode(buffer);
dictionary[key] = this.decode(buffer);
}
return dictionary;
}
else if (tag === 239 /* DataFormatTags.DICTIONARY_TERMINATED */) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dictionary = {};
let key;
while ((key = this.decode(buffer)) !== Magics.TERMINATOR) {
dictionary[key] = this.decode(buffer); // decode value
}
return dictionary;
}
else {
throw new Error("HDSDecoder: encountered unknown tag on index " + buffer.readerIndex + ": " + tag.toString(16));
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any
static encode(data, buffer) {
if (data === undefined) {
throw new Error("HDSEncoder: cannot encode undefined");
}
if (data === null) {
buffer.writeTag(4 /* DataFormatTags.NULL */);
return;
}
switch (typeof data) {
case "boolean":
if (data) {
buffer.writeTrue();
}
else {
buffer.writeFalse();
}
break;
case "number":
if (Number.isInteger(data)) {
buffer.writeNumber(data);
}
else {
buffer.writeFloat64LE(new Float64(data));
}
break;
case "string":
buffer.writeUTF8(data);
break;
case "object":
if (Array.isArray(data)) {
const length = data.length;
if (length <= 12) {
buffer.writeTag(208 /* DataFormatTags.ARRAY_LENGTH_START */ + length);
}
else {
buffer.writeTag(223 /* DataFormatTags.ARRAY_TERMINATED */);
}
data.forEach(element => {
this.encode(element, buffer);
});
if (length > 12) {
buffer.writeTag(3 /* DataFormatTags.TERMINATOR */);
}
}
else if (data instanceof ValueWrapper) {
if (data instanceof Int8) {
buffer.writeInt8(data);
}
else if (data instanceof Int16) {
buffer.writeInt16LE(data);
}
else if (data instanceof Int32) {
buffer.writeInt32LE(data);
}
else if (data instanceof Int64) {
buffer.writeInt64LE(data);
}
else if (data instanceof Float32) {
buffer.writeFloat32LE(data);
}
else if (data instanceof Float64) {
buffer.writeFloat64LE(data);
}
else if (data instanceof SecondsSince2001) {
buffer.writeSecondsSince2001_01_01(data);
}
else if (data instanceof UUID) {
buffer.writeUUID(data.value);
}
else {
throw new Error("Unknown wrapped object 'ValueWrapper' of class " + data.constructor.name);
}
}
else if (data instanceof Buffer) {
buffer.writeData(data);
}
else { // object is treated as dictionary
const entries = Object.entries(data)
.filter(entry => entry[1] !== undefined); // explicitly setting undefined will result in an entry here
if (entries.length <= 14) {
buffer.writeTag(224 /* DataFormatTags.DICTIONARY_LENGTH_START */ + entries.length);
}
else {
buffer.writeTag(239 /* DataFormatTags.DICTIONARY_TERMINATED */);
}
entries.forEach(entry => {
this.encode(entry[0], buffer); // encode key
this.encode(entry[1], buffer); // encode value
});
if (entries.length > 14) {
buffer.writeTag(3 /* DataFormatTags.TERMINATOR */);
}
}
break;
default:
throw new Error("HDSEncoder: no idea how to encode value of type '" + (typeof data) + "': " + data);
}
}
}
exports.DataStreamParser = DataStreamParser;
/**
* @group HomeKit Data Streams (HDS)
*/
class DataStreamReader {
data;
readerIndex;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
trackedCompressedData = [];
constructor(data) {
this.data = data;
this.readerIndex = 0;
}
finished() {
if (this.readerIndex < this.data.length) {
const remainingHex = this.data.slice(this.readerIndex, this.data.length).toString("hex");
debug("WARNING Finished reading HDS stream, but there are still %d bytes remaining () %s", this.data.length - this.readerIndex, remainingHex);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
decompressData(index) {
if (index >= this.trackedCompressedData.length) {
throw new Error("HDSDecoder: Tried decompression of data for an index out of range (index " + index +
" and got " + this.trackedCompressedData.length + " elements)");
}
return this.trackedCompressedData[index];
}
trackData(data) {
this.trackedCompressedData.push(data);
return data;
}
ensureLength(bytes) {
if (this.readerIndex + bytes > this.data.length) {
const remaining = this.data.length - this.readerIndex;
throw new Error("HDSDecoder: End of data stream. Tried reading " + bytes + " bytes however got only " + remaining + " remaining!");
}
}
readTag() {
this.ensureLength(1);
return this.data.readUInt8(this.readerIndex++);
}
readTrue() {
return this.trackData(true); // do those tag encoded values get cached?
}
readFalse() {
return this.trackData(false);
}
readNegOne() {
return this.trackData(-1);
}
readIntRange(tag) {
return this.trackData(tag - 8 /* DataFormatTags.INTEGER_RANGE_START_0 */); // integer values from 0-39
}
readInt8() {
this.ensureLength(1);
return this.trackData(this.data.readInt8(this.readerIndex++));
}
readInt16LE() {
this.ensureLength(2);
const value = this.data.readInt16LE(this.readerIndex);
this.readerIndex += 2;
return this.trackData(value);
}
readInt32LE() {
this.ensureLength(4);
const value = this.data.readInt32LE(this.readerIndex);
this.readerIndex += 4;
return this.trackData(value);
}
readInt64LE() {
this.ensureLength(8);
const low = this.data.readInt32LE(this.readerIndex);
let value = this.data.readInt32LE(this.readerIndex + 4) * 0x100000000 + low;
if (low < 0) {
value += 0x100000000;
}
this.readerIndex += 8;
return this.trackData(value);
}
readFloat32LE() {
this.ensureLength(4);
const value = this.data.readFloatLE(this.readerIndex);
this.readerIndex += 4;
return this.trackData(value);
}
readFloat64LE() {
this.ensureLength(8);
const value = this.data.readDoubleLE(this.readerIndex);
return this.trackData(value);
}
readLength8() {
this.ensureLength(1);
return this.data.readUInt8(this.readerIndex++);
}
readLength16LE() {
this.ensureLength(2);
const value = this.data.readUInt16LE(this.readerIndex);
this.readerIndex += 2;
return value;
}
readLength32LE() {
this.ensureLength(4);
const value = this.data.readUInt32LE(this.readerIndex);
this.readerIndex += 4;
return value;
}
readLength64LE() {
this.ensureLength(8);
const low = this.data.readUInt32LE(this.readerIndex);
const value = this.data.readUInt32LE(this.readerIndex + 4) * 0x100000000 + low;
this.readerIndex += 8;
return value;
}
readUTF8(length) {
this.ensureLength(length);
const value = this.data.toString("utf8", this.readerIndex, this.readerIndex + length);
this.readerIndex += length;
return this.trackData(value);
}
readUTF8_Length8() {
const length = this.readLength8();
return this.readUTF8(length);
}
readUTF8_Length16LE() {
const length = this.readLength16LE();
return this.readUTF8(length);
}
readUTF8_Length32LE() {
const length = this.readLength32LE();
return this.readUTF8(length);
}
readUTF8_Length64LE() {
const length = this.readLength64LE();
return this.readUTF8(length);
}
readUTF8_NULL_terminated() {
let offset = this.readerIndex;
let nextByte;
for (;;) {
nextByte = this.data[offset];
if (nextByte === undefined) {
throw new Error("HDSDecoder: Reached end of data stream while reading NUL terminated string!");
}
else if (nextByte === 0) {
break;
}
else {
offset++;
}
}
const value = this.data.toString("utf8", this.readerIndex, offset);
this.readerIndex = offset + 1;
return this.trackData(value);
}
readData(length) {
this.ensureLength(length);
const value = this.data.slice(this.readerIndex, this.readerIndex + length);
this.readerIndex += length;
return this.trackData(value);
}
readData_Length8() {
const length = this.readLength8();
return this.readData(length);
}
readData_Length16LE() {
const length = this.readLength16LE();
return this.readData(length);
}
readData_Length32LE() {
const length = this.readLength32LE();
return this.readData(length);
}
readData_Length64LE() {
const length = this.readLength64LE();
return this.readData(length);
}
readData_terminated() {
let offset = this.readerIndex;
let nextByte;
for (;;) {
nextByte = this.data[offset];
if (nextByte === undefined) {
throw new Error("HDSDecoder: Reached end of data stream while reading terminated data!");
}
else if (nextByte === 3 /* DataFormatTags.TERMINATOR */) {
break;
}
else {
offset++;
}
}
const value = this.data.slice(this.readerIndex, offset);
this.readerIndex = offset + 1;
return this.trackData(value);
}
readSecondsSince2001_01_01() {
// second since 2001-01-01 00:00:00
return this.readFloat64LE();
}
readUUID() {
this.ensureLength(16);
const value = uuid.unparse(this.data, this.readerIndex);
this.readerIndex += 16;
return this.trackData(value);
}
}
exports.DataStreamReader = DataStreamReader;
class WrittenDataList {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
writtenData = [];
push(data) {
this.writtenData.push(data);
}
indexOf(data) {
for (let i = 0; i < this.writtenData.length; i++) {
const data0 = this.writtenData[i];
if (data === data0) {
return i;
}
if (data instanceof ValueWrapper && data0 instanceof ValueWrapper) {
if (data.equals(data0)) {
return i;
}
}
}
return -1;
}
}
/**
* @group HomeKit Data Streams (HDS)
*/
class DataStreamWriter {
static chunkSize = 128; // seems to be a good default
data;
writerIndex;
writtenData = new WrittenDataList();
constructor() {
this.data = Buffer.alloc(DataStreamWriter.chunkSize);
this.writerIndex = 0;
}
length() {
return this.writerIndex; // since writerIndex points to the next FREE index it also represents the length
}
getData() {
return this.data.slice(0, this.writerIndex);
}
ensureLength(bytes) {
const neededBytes = (this.writerIndex + bytes) - this.data.length;
if (neededBytes > 0) {
const chunks = Math.ceil(neededBytes / DataStreamWriter.chunkSize);
// don't know if it's best for performance to immediately concatenate the buffers. That way it's
// the easiest way to handle writing though.
this.data = Buffer.concat([this.data, Buffer.alloc(chunks * DataStreamWriter.chunkSize)]);
}
}
compressDataIfPossible(data) {
const index = this.writtenData.indexOf(data);
if (index < 0) {
// data is not present yet
this.writtenData.push(data);
return false;
}
else if (index <= 207 /* DataFormatTags.COMPRESSION_STOP */ - 160 /* DataFormatTags.COMPRESSION_START */) {
// data was already written and the index is in the applicable range => shorten the payload
this.writeTag(160 /* DataFormatTags.COMPRESSION_START */ + index);
return true;
}
return false;
}
writeTag(tag) {
this.ensureLength(1);
this.data.writeUInt8(tag, this.writerIndex++);
}
writeTrue() {
this.writeTag(1 /* DataFormatTags.TRUE */);
}
writeFalse() {
this.writeTag(2 /* DataFormatTags.FALSE */);
}
writeNumber(number) {
if (number === -1) {
this.writeTag(7 /* DataFormatTags.INTEGER_MINUS_ONE */);
}
else if (number >= 0 && number <= 39) {
this.writeTag(8 /* DataFormatTags.INTEGER_RANGE_START_0 */ + number);
}
else if (number >= -128 && number <= 127) {
this.writeInt8(new Int8(number));
}
else if (number >= -32768 && number <= 32767) {
this.writeInt16LE(new Int16(number));
}
else if (number >= -2147483648 && number <= -2147483648) {
this.writeInt32LE(new Int32(number));
}
else if (number >= Number.MIN_SAFE_INTEGER && number <= Number.MAX_SAFE_INTEGER) { // use correct uin64 restriction when we convert to bigint
this.writeInt64LE(new Int64(number));
}
else {
throw new Error("Tried writing unrepresentable number (" + number + ")");
}
}
writeInt8(int8) {
if (this.compressDataIfPossible(int8)) {
return;
}
this.ensureLength(2);
this.writeTag(48 /* DataFormatTags.INT8 */);
this.data.writeInt8(int8.value, this.writerIndex++);
}
writeInt16LE(int16) {
if (this.compressDataIfPossible(int16)) {
return;
}
this.ensureLength(3);
this.writeTag(49 /* DataFormatTags.INT16LE */);
this.data.writeInt16LE(int16.value, this.writerIndex);
this.writerIndex += 2;
}
writeInt32LE(int32) {
if (this.compressDataIfPossible(int32)) {
return;
}
this.ensureLength(5);
this.writeTag(50 /* DataFormatTags.INT32LE */);
this.data.writeInt32LE(int32.value, this.writerIndex);
this.writerIndex += 4;
}
writeInt64LE(int64) {
if (this.compressDataIfPossible(int64)) {
return;
}
this.ensureLength(9);
this.writeTag(51 /* DataFormatTags.INT64LE */);
this.data.writeUInt32LE(int64.value, this.writerIndex); // TODO correctly implement int64; currently it's basically an int32
this.data.writeUInt32LE(0, this.writerIndex + 4);
this.writerIndex += 8;
}
writeFloat32LE(float32) {
if (this.compressDataIfPossible(float32)) {
return;
}
this.ensureLength(5);
this.writeTag(53 /* DataFormatTags.FLOAT32LE */);
this.data.writeFloatLE(float32.value, this.writerIndex);
this.writerIndex += 4;
}
writeFloat64LE(float64) {
if (this.compressDataIfPossible(float64)) {
return;
}
this.ensureLength(9);
this.writeTag(54 /* DataFormatTags.FLOAT64LE */);
this.data.writeDoubleLE(float64.value, this.writerIndex);
this.writerIndex += 8;
}
writeLength8(length) {
this.ensureLength(1);
this.data.writeUInt8(length, this.writerIndex++);
}
writeLength16LE(length) {
this.ensureLength(2);
this.data.writeUInt16LE(length, this.writerIndex);
this.writerIndex += 2;
}
writeLength32LE(length) {
this.ensureLength(4);
this.data.writeUInt32LE(length, this.writerIndex);
this.writerIndex += 4;
}
writeLength64LE(length) {
this.ensureLength(8);
hapCrypto.writeUInt64LE(length, this.data, this.writerIndex);
this.writerIndex += 8;
}
writeUTF8(utf8) {
if (this.compressDataIfPossible(utf8)) {
return;
}
const length = Buffer.byteLength(utf8);
if (length <= 32) {
this.ensureLength(1 + length);
this.writeTag(64 /* DataFormatTags.UTF8_LENGTH_START */ + utf8.length);
this._writeUTF8(utf8);
}
else if (length <= 255) {
this.writeUTF8_Length8(utf8);
}
else if (length <= 65535) {
this.writeUTF8_Length16LE(utf8);
}
else if (length <= 4294967295) {
this.writeUTF8_Length32LE(utf8);
}
else if (length <= Number.MAX_SAFE_INTEGER) { // use correct uin64 restriction when we convert to bigint
this.writeUTF8_Length64LE(utf8);
}
else {
this.writeUTF8_NULL_terminated(utf8);
}
}
_writeUTF8(utf8) {
const byteLength = Buffer.byteLength(utf8);
this.ensureLength(byteLength);
this.data.write(utf8, this.writerIndex, byteLength, "utf8");
this.writerIndex += byteLength;
}
writeUTF8_Length8(utf8) {
const length = Buffer.byteLength(utf8);
this.ensureLength(2 + length);
this.writeTag(97 /* DataFormatTags.UTF8_LENGTH8 */);
this.writeLength8(length);
this._writeUTF8(utf8);
}
writeUTF8_Length16LE(utf8) {
const length = Buffer.byteLength(utf8);
this.ensureLength(3 + length);
this.writeTag(98 /* DataFormatTags.UTF8_LENGTH16LE */);
this.writeLength16LE(length);
this._writeUTF8(utf8);
}
writeUTF8_Length32LE(utf8) {
const length = Buffer.byteLength(utf8);
this.ensureLength(5 + length);
this.writeTag(99 /* DataFormatTags.UTF8_LENGTH32LE */);
this.writeLength32LE(length);
this._writeUTF8(utf8);
}
writeUTF8_Length64LE(utf8) {
const length = Buffer.byteLength(utf8);
this.ensureLength(9 + length);
this.writeTag(100 /* DataFormatTags.UTF8_LENGTH64LE */);
this.writeLength64LE(length);
this._writeUTF8(utf8);
}
writeUTF8_NULL_terminated(utf8) {
this.ensureLength(1 + Buffer.byteLength(utf8) + 1);
this.writeTag(111 /* DataFormatTags.UTF8_NULL_TERMINATED */);
this._writeUTF8(utf8);
this.data.writeUInt8(0, this.writerIndex++);
}
writeData(data) {
if (this.compressDataIfPossible(data)) {
return;
}
if (data.length <= 32) {
this.writeTag(112 /* DataFormatTags.DATA_LENGTH_START */ + data.length);
this._writeData(data);
}
else if (data.length <= 255) {
this.writeData_Length8(data);
}
else if (data.length <= 65535) {
this.writeData_Length16LE(data);
}
else if (data.length <= 4294967295) {
this.writeData_Length32LE(data);
}
else if (data.length <= Number.MAX_SAFE_INTEGER) {
this.writeData_Length64LE(data);
}
else {
this.writeData_terminated(data);
}
}
_writeData(data) {
this.ensureLength(data.length);
for (let i = 0; i < data.length; i++) {
this.data[this.writerIndex++] = data[i];
}
}
writeData_Length8(data) {
this.ensureLength(2 + data.length);
this.writeTag(145 /* DataFormatTags.DATA_LENGTH8 */);
this.writeLength8(data.length);
this._writeData(data);
}
writeData_Length16LE(data) {
this.ensureLength(3 + data.length);
this.writeTag(146 /* DataFormatTags.DATA_LENGTH16LE */);
this.writeLength16LE(data.length);
this._writeData(data);
}
writeData_Length32LE(data) {
this.ensureLength(5 + data.length);
this.writeTag(147 /* DataFormatTags.DATA_LENGTH32LE */);
this.writeLength32LE(data.length);
this._writeData(data);
}
writeData_Length64LE(data) {
this.ensureLength(9 + data.length);
this.writeTag(148 /* DataFormatTags.DATA_LENGTH64LE */);
this.writeLength64LE(data.length);
this._writeData(data);
}
writeData_terminated(data) {
this.ensureLength(1 + data.length + 1);
this.writeTag(159 /* DataFormatTags.DATA_TERMINATED */);
this._writeData(data);
this.writeTag(3 /* DataFormatTags.TERMINATOR */);
}
writeSecondsSince2001_01_01(seconds) {
if (this.compressDataIfPossible(seconds)) {
return;
}
this.ensureLength(9);
this.writeTag(6 /* DataFormatTags.DATE */);
this.data.writeDoubleLE(seconds.value, this.writerIndex);
this.writerIndex += 8;
}
writeUUID(uuid_string) {
(0, assert_1.default)(uuid.isValid(uuid_string), "supplied uuid is invalid");
if (this.compressDataIfPossible(new UUID(uuid_string))) {
return;
}
this.ensureLength(17);
this.writeTag(5 /* DataFormatTags.UUID */);
uuid.write(uuid_string, this.data, this.writerIndex);
this.writerIndex += 16;
}
}
exports.DataStreamWriter = DataStreamWriter;
//# sourceMappingURL=DataStreamParser.js.map