json.js 19 KB

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