SOURCE / PINNED RELEASE
Made of little things.
Powder Tool V600Billion
- Release
- 142767edcab8…
- Author-recorded commit
- 6d92971effd0…
- License
- LICENSE
- Author’s source reference
- nostr://npub1fllw8kw0thjj55wds0uugcnp5kej2nfxd36eruq39d56wwz8r44q5q78wj/wss%3A%2F%2Fgit.napplet.soy%2F/powder-toy
Archive hash verified: ed7d6a8ea7083197…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/**
* A strict BSON reader for Powder Toy saves: every length is checked against
* the buffer before it is used, and nesting is bounded. Only the types the
* game writes are accepted.
*/
export class BsonError extends Error {
constructor(message) {
super(message);
this.name = 'BsonError';
}
}
const decoder = new TextDecoder();
export function parseBson(bytes, { maxDepth = 16 } = {}) {
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const need = (at, count) => {
if (at < 0 || count < 0 || at + count > bytes.length) throw new BsonError('Truncated document');
};
const cstring = (at) => {
let end = at;
while (end < bytes.length && bytes[end] !== 0) end++;
if (end >= bytes.length) throw new BsonError('Unterminated name');
return [decoder.decode(bytes.subarray(at, end)), end + 1];
};
function document(at, depth, asArray) {
if (depth > maxDepth) throw new BsonError('Document nested too deep');
need(at, 5);
const size = view.getInt32(at, true);
if (size < 5) throw new BsonError('Bad document size');
need(at, size);
const end = at + size - 1;
if (bytes[end] !== 0) throw new BsonError('Document not terminated');
const result = asArray ? [] : {};
let cursor = at + 4;
while (cursor < end) {
const type = bytes[cursor++];
let name;
[name, cursor] = cstring(cursor);
let value;
switch (type) {
case 0x01: need(cursor, 8); value = view.getFloat64(cursor, true); cursor += 8; break;
case 0x02: {
need(cursor, 4);
const length = view.getInt32(cursor, true);
if (length < 1) throw new BsonError('Bad string length');
need(cursor + 4, length);
value = decoder.decode(bytes.subarray(cursor + 4, cursor + 4 + length - 1));
cursor += 4 + length;
break;
}
case 0x03: case 0x04: {
need(cursor, 4);
const size = view.getInt32(cursor, true);
value = document(cursor, depth + 1, type === 0x04);
cursor += size;
break;
}
case 0x05: {
need(cursor, 5);
const length = view.getInt32(cursor, true);
if (length < 0) throw new BsonError('Bad binary length');
need(cursor + 5, length);
value = bytes.subarray(cursor + 5, cursor + 5 + length);
cursor += 5 + length;
break;
}
case 0x08: need(cursor, 1); value = bytes[cursor] !== 0; cursor += 1; break;
case 0x0a: value = null; break;
case 0x10: need(cursor, 4); value = view.getInt32(cursor, true); cursor += 4; break;
case 0x09: case 0x11: case 0x12: {
need(cursor, 8);
const big = view.getBigInt64(cursor, true);
value = big >= BigInt(Number.MIN_SAFE_INTEGER) && big <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(big) : big;
cursor += 8;
break;
}
default: throw new BsonError(`Unsupported BSON type 0x${type.toString(16)}`);
}
if (asArray) result.push(value);
else result[name] = value;
}
if (cursor !== end) throw new BsonError('Document size mismatch');
return result;
}
return document(0, 0, false);
}
