@bedrock-apis/nbt
Version:
Solid NBT package, with multiple formats
347 lines (343 loc) • 10.3 kB
JavaScript
import { UTF8_BUFFER_HELPER, UTF8_DECODER, UTF8_ENCODER } from "./shared-D4Le05PG.js";
import { Byte, Double, Float, Int, Short, TagType } from "@bedrock-apis/nbt-core";
//#region main/async/binary-source.ts
var BinaryDataTransformerInstance = class BinaryDataTransformerInstance {
pointer = 0;
length = 0;
view;
program;
controller;
requested = 0;
isDone = false;
constructor(buffer, maxSubChunkSize = buffer.length - 256) {
this.buffer = buffer;
this.maxSubChunkSize = maxSubChunkSize;
this.view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
}
start(controller) {
this.controller = controller;
this.program = this.getProgram(controller);
}
transform(chunk, controller) {
try {
if (this.isDone) return void controller.terminate();
for (const c of BinaryDataTransformerInstance.getChunkIterator(chunk, this.maxSubChunkSize)) {
this.flushBuffer();
this.set(c);
while (this.requested <= this.length - this.pointer) {
const { done, value } = this.program.next(this.length - this.pointer);
if (this.isDone = done) return void controller.terminate();
if ((this.requested = value) === -1) {
this.requested = this.length = this.pointer = 0;
this.isDone = true;
break;
}
}
}
} catch (error) {
controller.error(error);
}
}
flush() {
if (this.isDone) return;
try {
let nextValue = this.program.next(this.length - this.pointer);
while (!nextValue.done) nextValue = this.program.next(this.length - this.pointer);
} catch (error) {
this.controller.error(error);
}
this.controller.terminate();
}
set(u8) {
if (this.length + u8.length > this.buffer.byteLength) throw new Error(`Buffer overflow error, ${this.length}, ${this.buffer.byteLength}, ${u8.length}`);
this.buffer.set(u8, this.length);
this.length += u8.length;
}
flushBuffer() {
if (this.pointer <= 0 || this.length <= 0) return;
this.buffer.set(this.buffer.subarray(this.pointer, this.length), 0);
this.length -= this.pointer;
this.pointer = 0;
}
reset() {
this.length = 0;
this.pointer = 0;
}
static *getChunkIterator(buffer, chunkLength) {
let start = 0;
while (start < buffer.length) yield buffer.subarray(start, start += chunkLength);
}
createStreamController() {
let controller;
const readable = new ReadableStream({ start(c) {
controller = c;
} });
return {
controller,
readable
};
}
*bufferUpController(controller, length, close = true) {
let offset = 0;
while (offset < length) {
const available = yield 1;
let toRead = Math.min(available, length - offset);
if (toRead === 0) break;
controller.enqueue(this.rentSlice(toRead));
offset += toRead;
}
if (close) controller.close();
}
createReadable(length) {
const { controller, readable } = this.createStreamController();
const generator = this.bufferUpController(controller, length);
generator.readable = readable;
return generator;
}
*bufferUp(buffer) {
let offset = 0;
while (offset < buffer.length) {
const available = yield 1;
let toRead = Math.min(available, buffer.length - offset);
if (toRead === 0) return buffer;
buffer.set(this.rentSlice(toRead), offset);
offset += toRead;
}
return buffer;
}
*batchSkip(length) {
let offset = 0;
while (offset < length) {
const available = yield 1;
let toRead = Math.min(available, length - offset);
if (toRead === 0) return;
this.pointer += toRead;
offset += toRead;
}
}
rentSlice(length) {
const _ = this.buffer.subarray(this.pointer, this.pointer + length);
this.pointer += length;
return _;
}
rentDataView(length) {
const _ = new DataView(this.buffer.buffer, this.view.byteOffset + this.pointer, length);
this.pointer += length;
return _;
}
};
//#endregion
//#region main/async/binary-tokenizer.ts
let ReadMode = /* @__PURE__ */ function(ReadMode$1) {
ReadMode$1[ReadMode$1["RootTag"] = 241] = "RootTag";
ReadMode$1[ReadMode$1["ValueTag"] = 242] = "ValueTag";
return ReadMode$1;
}({});
const VALUE_SIZES = {
1: 1,
2: 2,
3: 4,
4: 8,
5: 5,
6: 8
};
const TYPE_TOKENS = {
1: Byte,
2: Short,
3: Int,
5: Float,
6: Double
};
var NBTTokenizerTransformer = class extends BinaryDataTransformerInstance {
constructor(buffer, format, mode) {
super(buffer, 256);
this.format = format;
this.mode = mode;
}
*getProgram(controller) {
let typeToRead = this.mode;
if (this.mode === ReadMode.ValueTag) {
yield 1;
typeToRead = this.format.readType(this);
}
if (this.mode === ReadMode.RootTag) {
yield 6;
typeToRead = this.format.readType(this);
yield* this.batchSkip(this.format.readStringLength(this));
}
yield* this.push(typeToRead);
}
*push(tag) {
if (tag in VALUE_SIZES) yield VALUE_SIZES[tag];
if (tag in TYPE_TOKENS) return void this.controller.enqueue(new TYPE_TOKENS[tag](this.format[tag](this)));
let length;
switch (tag) {
case 4: return void this.controller.enqueue(this.format[4](this));
case 8: return void (yield* this.pushString());
case 9: return void (yield* this.pushList());
case 10: return void (yield* this.pushCompound());
case 7:
yield 5;
length = this.format.readArrayLength(this);
return void this.controller.enqueue(yield* this.bufferUp(new Uint8Array(length)));
}
throw new ReferenceError("Type not supported yet, " + TagType[tag]);
}
*pushString() {
yield 5;
const length = this.format.readStringLength(this);
const buffer = new Uint8Array(length);
yield* this.bufferUp(buffer);
this.controller.enqueue(UTF8_DECODER.decode(buffer));
}
*pushList() {
yield 6;
const type = this.format.readType(this);
const length = this.format.readArrayLength(this);
if (type === 0 || length === 0) return void this.controller.enqueue({
type: 9,
length: 0,
tagType: type
});
if (!(type in this.format)) throw new SyntaxError("Unexpected NBT token type: " + type);
for (let i = 0; i < length; i++) yield* this.push(type);
return void this.controller.enqueue({
type: 9,
length,
tagType: type
});
}
*pushCompound() {
let count = 0;
let type;
yield 1;
while ((type = this.format.readType(this)) !== 0) {
yield 5;
const length = this.format.readStringLength(this);
if (length > this.maxSubChunkSize) throw new Error("Key Length is too big, max valid key name is: " + this.maxSubChunkSize);
yield length;
this.controller.enqueue(UTF8_DECODER.decode(this.rentSlice(length)));
yield* this.push(type);
count++;
yield 1;
}
this.controller.enqueue({
type: 10,
length: count
});
}
};
const BYTE = new Byte(0);
const GetTagType = (v) => {
BYTE.value = v;
return BYTE;
};
var NBTTokenize = class {
static *getRootIterator(value, rootKey = "") {
const type = value.internalTagType;
yield GetTagType(type);
yield rootKey;
if (type < 9) return void (yield* [value].values());
if (type in this) return void (yield* this[type](value));
throw new ReferenceError("Unknown or not serializable tag type: " + type);
}
static *10(value) {
for (const key in value) {
const valueType = value[key];
if (!isFinite(valueType.internalTagType ?? NaN)) continue;
yield GetTagType(valueType.internalTagType);
yield key;
if (valueType.internalTagType < 9) yield valueType;
else if (valueType.internalTagType in this) yield* this[valueType.internalTagType](valueType);
else throw new SyntaxError("No Serializer for " + valueType.internalTagType);
}
yield GetTagType(0);
}
static *9(value) {
if (value.length === 0) {
yield GetTagType(0);
yield new Int(0);
return;
}
const valueType = value[0];
const tagType = valueType.internalTagType;
if (tagType > 8 && !(tagType in this)) throw new ReferenceError("Not serializable tag type: " + tagType);
yield GetTagType(tagType);
yield new Int(value.length);
for (let i = 0; i < value.length; i++) {
const v = value[i];
const type = v?.internalTagType;
if (type !== tagType) throw new ReferenceError("Array has to be of only of the one type");
if (type < 9) yield v;
yield* this[type](v);
}
}
};
//#endregion
//#region main/async/binary-writer.ts
var TokenReader = class extends ReadableStream {
pointer = 0;
view;
get availableSpace() {
return this.buffer.length - this.pointer;
}
getWrittenBuffer() {
return this.buffer.slice(0, this.pointer);
}
pushChunk(controller, chunk) {
let read = 0;
while (read < chunk.length) {
if (this.pointer === 0 && chunk.length - read > this.buffer.length) {
controller.enqueue(chunk.slice(read, read += chunk.length - read));
continue;
}
const subchunk = chunk.subarray(read, read += this.availableSpace);
this.buffer.set(subchunk, this.pointer);
this.pointer += subchunk.length;
if (this.availableSpace === 0) this.flush(controller);
}
}
constructor(iterator, format, buffer = new Uint8Array(16384)) {
super({
pull: (controller) => {
while (this.pointer + 8 < this.buffer.length) {
let { value, done } = iterator.next();
if (done) {
this.flush(controller);
controller.close();
return;
}
if (value.internalTagType < 7) {
format[value.internalTagType](this, value.valueOf());
continue;
}
switch (value.internalTagType) {
case TagType.ByteArray:
format.writeArrayLength(this, value.length);
this.pushChunk(controller, value);
continue;
case TagType.String:
const v = UTF8_ENCODER.encodeInto(value.valueOf(), UTF8_BUFFER_HELPER);
format.writeStringLength(this, v.written);
this.pushChunk(controller, UTF8_BUFFER_HELPER.subarray(0, v.written));
continue;
default: throw new SyntaxError("Unexpected token type: " + value.internalTagType);
}
}
this.flush(controller);
},
type: "bytes"
}, { highWaterMark: 1024 });
this.format = format;
this.buffer = buffer;
this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
}
flush(controller) {
if (this.pointer) {
controller.enqueue(this.getWrittenBuffer());
this.pointer = 0;
}
}
};
//#endregion
export { BinaryDataTransformerInstance, NBTTokenize, NBTTokenizerTransformer, ReadMode, TYPE_TOKENS, TokenReader, VALUE_SIZES };