UNPKG

@bedrock-apis/nbt

Version:

Solid NBT package, with multiple formats

347 lines (343 loc) 10.3 kB
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 };