json.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. import * as text from './text.js';
  2. const json = {};
  3. const bson = {};
  4. json.TextReader = class {
  5. static open(data) {
  6. const decoder = text.Decoder.open(data);
  7. const position = decoder.position;
  8. let state = '';
  9. for (let i = 0; i < 0x1000; i++) {
  10. const c = decoder.decode();
  11. if (state === 'match') {
  12. break;
  13. }
  14. if (c === undefined || c === '\0') {
  15. if (state === '') {
  16. return null;
  17. }
  18. break;
  19. }
  20. if (c <= ' ') {
  21. if (c !== ' ' && c !== '\n' && c !== '\r' && c !== '\t') {
  22. return null;
  23. }
  24. continue;
  25. }
  26. switch (state) {
  27. case '':
  28. if (c === '{') {
  29. state = '{}';
  30. } else if (c === '[') {
  31. state = '[]';
  32. } else {
  33. return null;
  34. }
  35. break;
  36. case '[]':
  37. if (c !== '"' && c !== '-' && c !== '+' && c !== '{' && c !== '[' && c !== ']' && (c < '0' || c > '9')) {
  38. return null;
  39. }
  40. state = 'match';
  41. break;
  42. case '{}':
  43. if (c !== '"' && c !== '}') {
  44. return null;
  45. }
  46. state = 'match';
  47. break;
  48. default:
  49. break;
  50. }
  51. }
  52. decoder.position = position;
  53. return new json.TextReader(decoder);
  54. }
  55. constructor(decoder) {
  56. this._decoder = decoder;
  57. this._start = decoder.position;
  58. this._escape = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' };
  59. }
  60. read() {
  61. const stack = [];
  62. this._decoder.position = this._start;
  63. this._position = this._start;
  64. this._char = this._decoder.decode();
  65. this._whitespace();
  66. let obj = null;
  67. let first = true;
  68. for (;;) {
  69. if (Array.isArray(obj)) {
  70. this._whitespace();
  71. let c = this._char;
  72. if (c === ']') {
  73. this._next();
  74. this._whitespace();
  75. if (stack.length > 0) {
  76. obj = stack.pop();
  77. first = false;
  78. continue;
  79. }
  80. if (this._char !== undefined) {
  81. this._unexpected();
  82. }
  83. return obj;
  84. }
  85. if (!first) {
  86. if (this._char !== ',') {
  87. this._unexpected();
  88. }
  89. this._next();
  90. this._whitespace();
  91. c = this._char;
  92. }
  93. first = false;
  94. switch (c) {
  95. case '{': {
  96. this._next();
  97. stack.push(obj);
  98. const item = {};
  99. obj.push(item);
  100. obj = item;
  101. first = true;
  102. break;
  103. }
  104. case '[': {
  105. this._next();
  106. stack.push(obj);
  107. const item = [];
  108. obj.push(item);
  109. obj = item;
  110. first = true;
  111. break;
  112. }
  113. default: {
  114. obj.push(c === '"' ? this._string() : this._literal());
  115. break;
  116. }
  117. }
  118. } else if (obj instanceof Object) {
  119. this._whitespace();
  120. let c = this._char;
  121. if (c === '}') {
  122. this._next();
  123. this._whitespace();
  124. if (stack.length > 0) {
  125. obj = stack.pop();
  126. first = false;
  127. continue;
  128. }
  129. if (this._char !== undefined) {
  130. this._unexpected();
  131. }
  132. return obj;
  133. }
  134. if (!first) {
  135. if (this._char !== ',') {
  136. this._unexpected();
  137. }
  138. this._next();
  139. this._whitespace();
  140. c = this._char;
  141. }
  142. first = false;
  143. if (c === '"') {
  144. const key = this._string();
  145. switch (key) {
  146. case '__proto__':
  147. throw new json.Error(`Invalid key '${key}' ${this._location()}`);
  148. default:
  149. break;
  150. }
  151. this._whitespace();
  152. if (this._char !== ':') {
  153. this._unexpected();
  154. }
  155. this._next();
  156. this._whitespace();
  157. c = this._char;
  158. switch (c) {
  159. case '{': {
  160. this._next();
  161. stack.push(obj);
  162. const value = {};
  163. obj[key] = value;
  164. obj = value;
  165. first = true;
  166. break;
  167. }
  168. case '[': {
  169. this._next();
  170. stack.push(obj);
  171. const value = [];
  172. obj[key] = value;
  173. obj = value;
  174. first = true;
  175. break;
  176. }
  177. default: {
  178. obj[key] = c === '"' ? this._string() : this._literal();
  179. break;
  180. }
  181. }
  182. this._whitespace();
  183. continue;
  184. }
  185. this._unexpected();
  186. } else {
  187. const c = this._char;
  188. switch (c) {
  189. case '{': {
  190. this._next();
  191. this._whitespace();
  192. obj = {};
  193. first = true;
  194. break;
  195. }
  196. case '[': {
  197. this._next();
  198. this._whitespace();
  199. obj = [];
  200. first = true;
  201. break;
  202. }
  203. default: {
  204. let value = null;
  205. if (c === '"') {
  206. value = this._string();
  207. } else if (c >= '0' && c <= '9') {
  208. value = this._number();
  209. } else {
  210. value = this._literal();
  211. }
  212. this._whitespace();
  213. if (this._char !== undefined) {
  214. this._unexpected();
  215. }
  216. return value;
  217. }
  218. }
  219. }
  220. }
  221. }
  222. _next() {
  223. if (this._char === undefined) {
  224. this._unexpected();
  225. }
  226. this._position = this._decoder.position;
  227. this._char = this._decoder.decode();
  228. }
  229. _whitespace() {
  230. while (this._char === ' ' || this._char === '\n' || this._char === '\r' || this._char === '\t') {
  231. this._next();
  232. }
  233. }
  234. _literal() {
  235. const c = this._char;
  236. if (c >= '0' && c <= '9') {
  237. return this._number();
  238. }
  239. switch (c) {
  240. case 't': this._expect('true'); return true;
  241. case 'f': this._expect('false'); return false;
  242. case 'n': this._expect('null'); return null;
  243. case 'N': this._expect('NaN'); return NaN;
  244. case 'I': this._expect('Infinity'); return Infinity;
  245. case '-': return this._number();
  246. default: this._unexpected();
  247. }
  248. return null;
  249. }
  250. _number() {
  251. let value = '';
  252. if (this._char === '-') {
  253. value = '-';
  254. this._next();
  255. }
  256. if (this._char === 'I') {
  257. this._expect('Infinity');
  258. return -Infinity;
  259. }
  260. const c = this._char;
  261. if (c < '0' || c > '9') {
  262. this._unexpected();
  263. }
  264. value += c;
  265. this._next();
  266. if (c === '0') {
  267. const n = this._char;
  268. if (n >= '0' && n <= '9') {
  269. this._unexpected();
  270. }
  271. }
  272. while (this._char >= '0' && this._char <= '9') {
  273. value += this._char;
  274. this._next();
  275. }
  276. if (this._char === '.') {
  277. value += '.';
  278. this._next();
  279. const n = this._char;
  280. if (n < '0' || n > '9') {
  281. this._unexpected();
  282. }
  283. while (this._char >= '0' && this._char <= '9') {
  284. value += this._char;
  285. this._next();
  286. }
  287. }
  288. if (this._char === 'e' || this._char === 'E') {
  289. value += this._char;
  290. this._next();
  291. const s = this._char;
  292. if (s === '-' || s === '+') {
  293. value += this._char;
  294. this._next();
  295. }
  296. const c = this._char;
  297. if (c < '0' || c > '9') {
  298. this._unexpected();
  299. }
  300. value += this._char;
  301. this._next();
  302. while (this._char >= '0' && this._char <= '9') {
  303. value += this._char;
  304. this._next();
  305. }
  306. }
  307. return Number(value);
  308. }
  309. _string() {
  310. let value = '';
  311. this._next();
  312. while (this._char !== '"') {
  313. if (this._char === '\\') {
  314. this._next();
  315. if (this._char === 'u') {
  316. this._next();
  317. let uffff = 0;
  318. for (let i = 0; i < 4; i ++) {
  319. const hex = parseInt(this._char, 16);
  320. if (!isFinite(hex)) {
  321. this._unexpected();
  322. }
  323. this._next();
  324. uffff = uffff * 16 + hex;
  325. }
  326. value += String.fromCharCode(uffff);
  327. } else if (this._escape[this._char]) {
  328. value += this._escape[this._char];
  329. this._next();
  330. } else {
  331. this._unexpected();
  332. }
  333. } else if (this._char < ' ') {
  334. this._unexpected();
  335. } else {
  336. value += this._char;
  337. this._next();
  338. }
  339. }
  340. this._next();
  341. return value;
  342. }
  343. _expect(value) {
  344. for (let i = 0; i < value.length; i++) {
  345. if (value[i] !== this._char) {
  346. this._unexpected();
  347. }
  348. this._next();
  349. }
  350. }
  351. _unexpected() {
  352. let c = this._char;
  353. if (c === undefined) {
  354. throw new json.Error('Unexpected end of JSON input.');
  355. } else if (c === '"') {
  356. c = 'string';
  357. } else if ((c >= '0' && c <= '9') || c === '-') {
  358. c = 'number';
  359. } else {
  360. if (c < ' ' || c > '\x7F') {
  361. const name = Object.keys(this._escape).filter((key) => this._escape[key] === c);
  362. c = (name.length === 1) ? `\\${name}` : `\\u${(`000${c.charCodeAt(0).toString(16)}`).slice(-4)}`;
  363. }
  364. c = `token '${c}'`;
  365. }
  366. throw new json.Error(`Unexpected ${c} ${this._location()}`);
  367. }
  368. _location() {
  369. let line = 1;
  370. let column = 1;
  371. this._decoder.position = this._start;
  372. let c = '';
  373. do {
  374. if (this._decoder.position === this._position) {
  375. return `at ${line}:${column}.`;
  376. }
  377. c = this._decoder.decode();
  378. if (c === '\n') {
  379. line++;
  380. column = 1;
  381. } else {
  382. column++;
  383. }
  384. }
  385. while (c !== undefined);
  386. return `at ${line}:${column}.`;
  387. }
  388. };
  389. json.BinaryReader = class {
  390. static open(data) {
  391. const length = data.length;
  392. const buffer = data instanceof Uint8Array ? data : data.peek(Math.min(data.length, 8));
  393. const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  394. if (buffer.length >= 4 && view.getInt32(0, true) === length) {
  395. return new json.BinaryReader(data, 'bson');
  396. }
  397. const [signature] = buffer;
  398. if (signature === 0x7B || signature === 0x5B) {
  399. return new json.BinaryReader(data, 'ubj');
  400. }
  401. return null;
  402. }
  403. constructor(data, format) {
  404. this._buffer = data instanceof Uint8Array ? data : data.peek();
  405. this._format = format;
  406. }
  407. read() {
  408. switch (this._format) {
  409. case 'bson':
  410. return this._readBson();
  411. case 'ubj':
  412. return this._readUbj();
  413. default:
  414. throw new json.Error(`Unsupported binary JSON format '${this._format}'.`);
  415. }
  416. }
  417. _readBson() {
  418. const buffer = this._buffer;
  419. const length = buffer.length;
  420. const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  421. const asciiDecoder = new TextDecoder('ascii');
  422. const utf8Decoder = new TextDecoder('utf-8');
  423. let position = 0;
  424. const skip = (offset) => {
  425. position += offset;
  426. if (position > length) {
  427. throw new bson.Error(`Expected ${position + length} more bytes. The file might be corrupted. Unexpected end of file.`);
  428. }
  429. };
  430. const header = () => {
  431. const start = position;
  432. skip(4);
  433. const size = view.getInt32(start, 4);
  434. if (size < 5 || start + size > length || buffer[start + size - 1] !== 0x00) {
  435. throw new bson.Error('Invalid file size.');
  436. }
  437. };
  438. header();
  439. const stack = [];
  440. let obj = {};
  441. for (;;) {
  442. skip(1);
  443. const type = buffer[position - 1];
  444. if (type === 0x00) {
  445. if (stack.length === 0) {
  446. break;
  447. }
  448. obj = stack.pop();
  449. continue;
  450. }
  451. const start = position;
  452. position = buffer.indexOf(0x00, start);
  453. if (position === -1) {
  454. throw new bson.Error('Missing string terminator.');
  455. }
  456. position += 1;
  457. const key = asciiDecoder.decode(buffer.subarray(start, position - 1));
  458. let value = null;
  459. switch (type) {
  460. case 0x01: { // float64
  461. const start = position;
  462. skip(8);
  463. value = view.getFloat64(start, true);
  464. break;
  465. }
  466. case 0x02: { // string
  467. skip(4);
  468. const size = view.getInt32(position - 4, true);
  469. const start = position;
  470. skip(size);
  471. value = utf8Decoder.decode(buffer.subarray(start, position - 1));
  472. if (buffer[position - 1] !== 0) {
  473. throw new bson.Error('String missing terminal 0.');
  474. }
  475. break;
  476. }
  477. case 0x03: { // object
  478. header();
  479. value = {};
  480. break;
  481. }
  482. case 0x04: { // array
  483. header();
  484. value = [];
  485. break;
  486. }
  487. case 0x05: { // bytes
  488. const start = position;
  489. skip(5);
  490. const size = view.getInt32(start, true);
  491. const subtype = buffer[start + 4];
  492. if (subtype !== 0x00) {
  493. throw new bson.Error(`Unsupported binary subtype '${subtype}'.`);
  494. }
  495. skip(size);
  496. value = buffer.subarray(start + 5, position);
  497. break;
  498. }
  499. case 0x08: { // boolean
  500. skip(1);
  501. value = buffer[position - 1];
  502. if (value > 1) {
  503. throw new bson.Error(`Invalid boolean value '${value}'.`);
  504. }
  505. value = value === 1 ? true : false;
  506. break;
  507. }
  508. case 0x0A:
  509. value = null;
  510. break;
  511. case 0x10: {
  512. const start = position;
  513. skip(4);
  514. value = view.getInt32(start, true);
  515. break;
  516. }
  517. case 0x11: { // uint64
  518. const start = position;
  519. skip(8);
  520. value = view.getBigUint64(start, true);
  521. break;
  522. }
  523. case 0x12: { // int64
  524. const start = position;
  525. skip(8);
  526. value = view.getBigInt64(start, true);
  527. break;
  528. }
  529. default: {
  530. throw new bson.Error(`Unsupported value type '${type}'.`);
  531. }
  532. }
  533. if (Array.isArray(obj)) {
  534. if (obj.length !== parseInt(key, 10)) {
  535. throw new bson.Error(`Invalid array index '${key}'.`);
  536. }
  537. obj.push(value);
  538. } else {
  539. switch (key) {
  540. case '__proto__':
  541. case 'constructor':
  542. case 'prototype':
  543. throw new bson.Error(`Invalid key '${key}' at ${position}'.`);
  544. default:
  545. break;
  546. }
  547. obj[key] = value;
  548. }
  549. if (type === 0x03 || type === 0x04) {
  550. stack.push(obj);
  551. obj = value;
  552. }
  553. }
  554. if (position !== length) {
  555. throw new bson.Error(`Unexpected data at '${position}'.`);
  556. }
  557. return obj;
  558. }
  559. _readUbj() {
  560. throw new json.Error('Unsupported JSON UBJ data.');
  561. }
  562. };
  563. json.Error = class extends Error {
  564. constructor(message) {
  565. super(message);
  566. this.name = 'JSON Error';
  567. }
  568. };
  569. bson.Error = class extends Error {
  570. constructor(message) {
  571. super(message);
  572. this.name = 'BSON Error';
  573. }
  574. };
  575. export const TextReader = json.TextReader;
  576. export const BinaryReader = json.BinaryReader;