seroval 0.4.0-alpha.7 → 0.4.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,21 @@ import {
8
8
  } from '../context';
9
9
  import quote from '../quote';
10
10
  import { isReferenceInStack } from './shared';
11
- import { SerovalDictionaryNode, SerovalNode, SerovalNodeType } from './types';
11
+ import {
12
+ SerovalAggregateErrorNode,
13
+ SerovalArrayNode,
14
+ SerovalBigIntTypedArrayNode,
15
+ SerovalErrorNode,
16
+ SerovalIterableNode,
17
+ SerovalMapNode,
18
+ SerovalNode,
19
+ SerovalNodeType,
20
+ SerovalNullConstructorNode,
21
+ SerovalObjectRecordNode,
22
+ SerovalPromiseNode,
23
+ SerovalSetNode,
24
+ SerovalTypedArrayNode,
25
+ } from './types';
12
26
 
13
27
  function getAssignmentExpression(assignment: Assignment): string {
14
28
  switch (assignment.type) {
@@ -96,7 +110,7 @@ function assignRef(
96
110
  index: number,
97
111
  value: string,
98
112
  ) {
99
- if (ctx.markedRefs.has(index)) {
113
+ if (ctx.markedRefs[index]) {
100
114
  return `${getRefParam(ctx, index)}=${value}`;
101
115
  }
102
116
  return value;
@@ -167,16 +181,16 @@ const IDENTIFIER_CHECK = /^([$A-Z_][0-9A-Z_$]*)$/i;
167
181
  function serializeAssignments(
168
182
  ctx: SerializationContext,
169
183
  targetRef: number,
170
- [keys, values, size]: SerovalDictionaryNode,
184
+ node: SerovalObjectRecordNode,
171
185
  ) {
172
186
  ctx.stack.push(targetRef);
173
187
  const mainAssignments: Assignment[] = [];
174
- for (let i = 0; i < size; i++) {
188
+ for (let i = 0; i < node.s; i++) {
175
189
  const parentStack = ctx.stack;
176
190
  ctx.stack = [];
177
- const refParam = serializeTree(ctx, values[i]);
191
+ const refParam = serializeTree(ctx, node.v[i]);
178
192
  ctx.stack = parentStack;
179
- const key = keys[i];
193
+ const key = node.k[i];
180
194
  const check = Number(key);
181
195
  const parentAssignment = ctx.assignments;
182
196
  ctx.assignments = mainAssignments;
@@ -197,22 +211,22 @@ function serializeAssignments(
197
211
  function serializeObject(
198
212
  ctx: SerializationContext,
199
213
  sourceID: number,
200
- [keys, values, size]: SerovalDictionaryNode,
214
+ node: SerovalObjectRecordNode,
201
215
  ) {
202
- if (size === 0) {
216
+ if (node.s === 0) {
203
217
  return '{}';
204
218
  }
205
219
  let result = '';
206
220
  ctx.stack.push(sourceID);
207
- for (let i = 0; i < size; i++) {
208
- const key = keys[i];
209
- const val = values[i];
221
+ for (let i = 0; i < node.s; i++) {
222
+ const key = node.k[i];
223
+ const val = node.v[i];
210
224
  const check = Number(key);
211
225
  // Test if key is a valid number or JS identifier
212
226
  // so that we don't have to serialize the key and wrap with brackets
213
227
  const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
214
228
  if (isReferenceInStack(ctx, val)) {
215
- const refParam = getRefParam(ctx, val[1]);
229
+ const refParam = getRefParam(ctx, val.i);
216
230
  if (isIdentifier && Number.isNaN(check)) {
217
231
  createObjectAssign(ctx, sourceID, key, refParam);
218
232
  } else {
@@ -228,55 +242,50 @@ function serializeObject(
228
242
 
229
243
  function serializePromise(
230
244
  ctx: SerializationContext,
231
- value: SerovalNode,
232
- id: number,
245
+ node: SerovalPromiseNode,
233
246
  ) {
234
247
  let serialized: string;
235
248
  // Check if resolved value is a parent expression
236
- if (isReferenceInStack(ctx, value)) {
249
+ if (isReferenceInStack(ctx, node.n)) {
237
250
  // A Promise trick, reference the value
238
251
  // inside the `then` expression so that
239
252
  // the Promise evaluates after the parent
240
253
  // has initialized
241
- serialized = `Promise.resolve().then(()=>${getRefParam(ctx, value[1])})`;
254
+ serialized = `Promise.resolve().then(()=>${getRefParam(ctx, node.n.i)})`;
242
255
  } else {
243
- ctx.stack.push(id);
244
- const result = serializeTree(ctx, value);
256
+ ctx.stack.push(node.i);
257
+ const result = serializeTree(ctx, node.n);
245
258
  ctx.stack.pop();
246
259
  // just inline the value/reference here
247
260
  serialized = `Promise.resolve(${result})`;
248
261
  }
249
- return assignRef(ctx, id, serialized);
262
+ return assignRef(ctx, node.i, serialized);
250
263
  }
251
264
 
252
265
  function serializeTypedArray(
253
266
  ctx: SerializationContext,
254
- constructor: string,
255
- init: string,
256
- byteOffset: number,
257
- id: number,
267
+ node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
258
268
  ) {
259
- let args = `[${init}]`;
260
- if (byteOffset !== 0) {
261
- args += `,${byteOffset}`;
269
+ let args = `[${node.s}]`;
270
+ if (node.l !== 0) {
271
+ args += `,${node.l}`;
262
272
  }
263
- return assignRef(ctx, id, `new ${constructor}(${args})`);
273
+ return assignRef(ctx, node.i, `new ${node.c}(${args})`);
264
274
  }
265
275
 
266
276
  function serializeSet(
267
277
  ctx: SerializationContext,
268
- value: SerovalNode[],
269
- id: number,
278
+ node: SerovalSetNode,
270
279
  ) {
271
280
  let serialized = 'new Set';
272
- const size = value.length;
281
+ const size = node.a.length;
273
282
  if (size) {
274
283
  let result = '';
275
- ctx.stack.push(id);
284
+ ctx.stack.push(node.i);
276
285
  for (let i = 0; i < size; i++) {
277
- const item = value[i];
286
+ const item = node.a[i];
278
287
  if (isReferenceInStack(ctx, item)) {
279
- createSetAdd(ctx, id, getRefParam(ctx, item[1]));
288
+ createSetAdd(ctx, node.i, getRefParam(ctx, item.i));
280
289
  } else {
281
290
  // Push directly
282
291
  result += `${serializeTree(ctx, item)},`;
@@ -287,34 +296,31 @@ function serializeSet(
287
296
  serialized += `([${result.substring(0, result.length - 1)}])`;
288
297
  }
289
298
  }
290
- return assignRef(ctx, id, serialized);
299
+ return assignRef(ctx, node.i, serialized);
291
300
  }
292
301
 
293
302
  function serializeMap(
294
303
  ctx: SerializationContext,
295
- keys: SerovalNode[],
296
- values: SerovalNode[],
297
- size: number,
298
- id: number,
304
+ node: SerovalMapNode,
299
305
  ) {
300
306
  let serialized = 'new Map';
301
- if (size) {
307
+ if (node.d.s) {
302
308
  let result = '';
303
- ctx.stack.push(id);
304
- for (let i = 0; i < size; i++) {
309
+ ctx.stack.push(node.i);
310
+ for (let i = 0; i < node.d.s; i++) {
305
311
  // Check if key is a parent
306
- const key = keys[i];
307
- const val = values[i];
312
+ const key = node.d.k[i];
313
+ const val = node.d.v[i];
308
314
  if (isReferenceInStack(ctx, key)) {
309
315
  // Create reference for the map instance
310
- const keyRef = getRefParam(ctx, key[1]);
316
+ const keyRef = getRefParam(ctx, key.i);
311
317
  // Check if value is a parent
312
318
  if (isReferenceInStack(ctx, val)) {
313
- const valueRef = getRefParam(ctx, val[1]);
319
+ const valueRef = getRefParam(ctx, val.i);
314
320
  // Register an assignment since
315
321
  // both key and value are a parent of this
316
322
  // Map instance
317
- createMapSet(ctx, id, keyRef, valueRef);
323
+ createMapSet(ctx, node.i, keyRef, valueRef);
318
324
  } else {
319
325
  // Reset the stack
320
326
  // This is required because the serialized
@@ -323,16 +329,16 @@ function serializeMap(
323
329
  // assignment
324
330
  const parent = ctx.stack;
325
331
  ctx.stack = [];
326
- createMapSet(ctx, id, keyRef, serializeTree(ctx, val));
332
+ createMapSet(ctx, node.i, keyRef, serializeTree(ctx, val));
327
333
  ctx.stack = parent;
328
334
  }
329
335
  } else if (isReferenceInStack(ctx, val)) {
330
336
  // Create ref for the Map instance
331
- const valueRef = getRefParam(ctx, val[1]);
337
+ const valueRef = getRefParam(ctx, val.i);
332
338
  // Reset stack for the key serialization
333
339
  const parent = ctx.stack;
334
340
  ctx.stack = [];
335
- createMapSet(ctx, id, serializeTree(ctx, key), valueRef);
341
+ createMapSet(ctx, node.i, serializeTree(ctx, key), valueRef);
336
342
  ctx.stack = parent;
337
343
  } else {
338
344
  result += `[${serializeTree(ctx, key)},${serializeTree(ctx, val)}],`;
@@ -346,26 +352,25 @@ function serializeMap(
346
352
  serialized += `([${result.substring(0, result.length - 1)}])`;
347
353
  }
348
354
  }
349
- return assignRef(ctx, id, serialized);
355
+ return assignRef(ctx, node.i, serialized);
350
356
  }
351
357
 
352
358
  function serializeNodeList(
353
359
  ctx: SerializationContext,
354
- value: SerovalNode[],
355
- id: number,
360
+ node: SerovalArrayNode | SerovalIterableNode,
356
361
  ) {
357
362
  // This is different than Map and Set
358
363
  // because we also need to serialize
359
364
  // the holes of the Array
360
- const size = value.length;
365
+ const size = node.a.length;
361
366
  let values = '';
362
367
  for (let i = 0; i < size; i++) {
363
- const item = value[i];
368
+ const item = node.a[i];
364
369
  // Check if index is a hole
365
370
  if (item) {
366
371
  // Check if item is a parent
367
372
  if (isReferenceInStack(ctx, item)) {
368
- createArrayAssign(ctx, id, i, getRefParam(ctx, item[1]));
373
+ createArrayAssign(ctx, node.i, i, getRefParam(ctx, item.i));
369
374
  values += ',';
370
375
  } else {
371
376
  values += serializeTree(ctx, item);
@@ -383,18 +388,17 @@ function serializeNodeList(
383
388
 
384
389
  function serializeArray(
385
390
  ctx: SerializationContext,
386
- value: SerovalNode[],
387
- id: number,
391
+ node: SerovalArrayNode,
388
392
  ) {
389
- ctx.stack.push(id);
390
- const result = serializeNodeList(ctx, value, id);
393
+ ctx.stack.push(node.i);
394
+ const result = serializeNodeList(ctx, node);
391
395
  ctx.stack.pop();
392
- return assignRef(ctx, id, result);
396
+ return assignRef(ctx, node.i, result);
393
397
  }
394
398
 
395
399
  function serializeWithObjectAssign(
396
400
  ctx: SerializationContext,
397
- value: SerovalDictionaryNode,
401
+ value: SerovalObjectRecordNode,
398
402
  id: number,
399
403
  serialized: string,
400
404
  ) {
@@ -407,65 +411,57 @@ function serializeWithObjectAssign(
407
411
 
408
412
  function serializeAggregateError(
409
413
  ctx: SerializationContext,
410
- message: string,
411
- options: SerovalDictionaryNode | undefined,
412
- errors: SerovalNode,
413
- id: number,
414
+ node: SerovalAggregateErrorNode,
414
415
  ) {
415
416
  // Serialize the required arguments
416
- ctx.stack.push(id);
417
- let serialized = `new AggregateError(${serializeTree(ctx, errors)},${quote(message)})`;
417
+ ctx.stack.push(node.i);
418
+ let serialized = `new AggregateError(${serializeTree(ctx, node.n)},${quote(node.m)})`;
418
419
  ctx.stack.pop();
419
420
  // `AggregateError` might've been extended
420
421
  // either through class or custom properties
421
422
  // Make sure to assign extra properties
422
- if (options) {
423
+ if (node.d) {
423
424
  if (ctx.features & Feature.ObjectAssign) {
424
- serialized = serializeWithObjectAssign(ctx, options, id, serialized);
425
+ serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized);
425
426
  } else {
426
- markRef(ctx, id);
427
- const assignments = serializeAssignments(ctx, id, options);
427
+ markRef(ctx, node.i);
428
+ const assignments = serializeAssignments(ctx, node.i, node.d);
428
429
  if (assignments) {
429
- const ref = getRefParam(ctx, id);
430
- return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
430
+ const ref = getRefParam(ctx, node.i);
431
+ return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`;
431
432
  }
432
433
  }
433
434
  }
434
- return assignRef(ctx, id, serialized);
435
+ return assignRef(ctx, node.i, serialized);
435
436
  }
436
437
 
437
438
  function serializeError(
438
439
  ctx: SerializationContext,
439
- constructor: string,
440
- message: string,
441
- options: SerovalDictionaryNode | undefined,
442
- id: number,
440
+ node: SerovalErrorNode,
443
441
  ) {
444
- let serialized = `new ${constructor}(${quote(message)})`;
445
- if (options) {
442
+ let serialized = `new ${node.c}(${quote(node.m)})`;
443
+ if (node.d) {
446
444
  if (ctx.features & Feature.ObjectAssign) {
447
- serialized = serializeWithObjectAssign(ctx, options, id, serialized);
445
+ serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized);
448
446
  } else {
449
- markRef(ctx, id);
450
- const assignments = serializeAssignments(ctx, id, options);
447
+ markRef(ctx, node.i);
448
+ const assignments = serializeAssignments(ctx, node.i, node.d);
451
449
  if (assignments) {
452
- const ref = getRefParam(ctx, id);
453
- return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
450
+ const ref = getRefParam(ctx, node.i);
451
+ return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`;
454
452
  }
455
453
  }
456
454
  }
457
- return assignRef(ctx, id, serialized);
455
+ return assignRef(ctx, node.i, serialized);
458
456
  }
459
457
 
460
458
  function serializeIterable(
461
459
  ctx: SerializationContext,
462
- options: SerovalDictionaryNode | undefined,
463
- items: SerovalNode[],
464
- id: number,
460
+ node: SerovalIterableNode,
465
461
  ) {
466
462
  const parent = ctx.stack;
467
463
  ctx.stack = [];
468
- const values = serializeNodeList(ctx, items, id);
464
+ const values = serializeNodeList(ctx, node);
469
465
  ctx.stack = parent;
470
466
  let serialized: string;
471
467
  if (ctx.features & Feature.ArrayPrototypeValues) {
@@ -480,76 +476,75 @@ function serializeIterable(
480
476
  } else {
481
477
  serialized = `{[Symbol.iterator]:function(){return ${serialized}}}`;
482
478
  }
483
- if (options) {
479
+ if (node.d) {
484
480
  if (ctx.features & Feature.ObjectAssign) {
485
- serialized = serializeWithObjectAssign(ctx, options, id, serialized);
481
+ serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized);
486
482
  } else {
487
- markRef(ctx, id);
488
- const assignments = serializeAssignments(ctx, id, options);
483
+ markRef(ctx, node.i);
484
+ const assignments = serializeAssignments(ctx, node.i, node.d);
489
485
  if (assignments) {
490
- const ref = getRefParam(ctx, id);
491
- return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
486
+ const ref = getRefParam(ctx, node.i);
487
+ return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`;
492
488
  }
493
489
  }
494
490
  }
495
- return assignRef(ctx, id, serialized);
491
+ return assignRef(ctx, node.i, serialized);
496
492
  }
497
493
 
498
494
  function serializeNullConstructor(
499
495
  ctx: SerializationContext,
500
- value: SerovalDictionaryNode,
501
- id: number,
496
+ node: SerovalNullConstructorNode,
502
497
  ) {
503
498
  let serialized = 'Object.create(null)';
504
499
  if (ctx.features & Feature.ObjectAssign) {
505
- serialized = serializeWithObjectAssign(ctx, value, id, serialized);
500
+ serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized);
506
501
  } else {
507
- markRef(ctx, id);
508
- const assignments = serializeAssignments(ctx, id, value);
502
+ markRef(ctx, node.i);
503
+ const assignments = serializeAssignments(ctx, node.i, node.d);
509
504
  if (assignments) {
510
- const ref = getRefParam(ctx, id);
511
- return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
505
+ const ref = getRefParam(ctx, node.i);
506
+ return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`;
512
507
  }
513
508
  }
514
- return assignRef(ctx, id, serialized);
509
+ return assignRef(ctx, node.i, serialized);
515
510
  }
516
511
 
517
512
  export default function serializeTree(
518
513
  ctx: SerializationContext,
519
- [type, value, id]: SerovalNode,
514
+ node: SerovalNode,
520
515
  ): string {
521
- switch (type) {
516
+ switch (node.t) {
522
517
  case SerovalNodeType.Primitive:
523
- return String(value);
518
+ return String(node.s);
524
519
  case SerovalNodeType.Reference:
525
- return getRefParam(ctx, value);
520
+ return getRefParam(ctx, node.i);
526
521
  case SerovalNodeType.Promise:
527
- return serializePromise(ctx, value, id);
522
+ return serializePromise(ctx, node);
528
523
  case SerovalNodeType.BigInt:
529
- return value;
524
+ return node.s;
530
525
  case SerovalNodeType.Date:
531
- return assignRef(ctx, id, `new Date("${value}")`);
526
+ return assignRef(ctx, node.i, `new Date("${node.s}")`);
532
527
  case SerovalNodeType.RegExp:
533
- return assignRef(ctx, id, value);
528
+ return assignRef(ctx, node.i, node.s);
534
529
  case SerovalNodeType.BigIntTypedArray:
535
530
  case SerovalNodeType.TypedArray:
536
- return serializeTypedArray(ctx, value[0], value[1], value[2], id);
531
+ return serializeTypedArray(ctx, node);
537
532
  case SerovalNodeType.Set:
538
- return serializeSet(ctx, value, id);
533
+ return serializeSet(ctx, node);
539
534
  case SerovalNodeType.Map:
540
- return serializeMap(ctx, value[0], value[1], value[2], id);
535
+ return serializeMap(ctx, node);
541
536
  case SerovalNodeType.Array:
542
- return serializeArray(ctx, value, id);
537
+ return serializeArray(ctx, node);
543
538
  case SerovalNodeType.AggregateError:
544
- return serializeAggregateError(ctx, value[0], value[1], value[2], id);
539
+ return serializeAggregateError(ctx, node);
545
540
  case SerovalNodeType.Error:
546
- return serializeError(ctx, value[0], value[1], value[2], id);
541
+ return serializeError(ctx, node);
547
542
  case SerovalNodeType.Iterable:
548
- return serializeIterable(ctx, value[0], value[1], id);
543
+ return serializeIterable(ctx, node);
549
544
  case SerovalNodeType.NullConstructor:
550
- return serializeNullConstructor(ctx, value, id);
545
+ return serializeNullConstructor(ctx, node);
551
546
  case SerovalNodeType.Object:
552
- return assignRef(ctx, id, serializeObject(ctx, id, value));
547
+ return assignRef(ctx, node.i, serializeObject(ctx, node.i, node.d));
553
548
  default:
554
549
  throw new Error('Unsupported type');
555
550
  }
@@ -67,7 +67,7 @@ export function isReferenceInStack(
67
67
  ctx: SerializationContext,
68
68
  node: SerovalNode,
69
69
  ): node is SerovalReferenceNode {
70
- return node[0] === SerovalNodeType.Reference && ctx.stack.includes(node[1]);
70
+ return node.t === SerovalNodeType.Reference && ctx.stack.includes(node.i);
71
71
  }
72
72
 
73
73
  export function generateRef(
@@ -82,7 +82,17 @@ export function generateRef(
82
82
  const refID = ctx.refs.get(current) || 0;
83
83
  // Mark reference
84
84
  markRef(ctx, refID);
85
- return [SerovalNodeType.Reference, refID];
85
+ return {
86
+ t: SerovalNodeType.Reference,
87
+ i: refID,
88
+ a: undefined,
89
+ s: undefined,
90
+ l: undefined,
91
+ m: undefined,
92
+ c: undefined,
93
+ d: undefined,
94
+ n: undefined,
95
+ };
86
96
  }
87
97
  // Create a new reference ID
88
98
  const id = ctx.refs.size;
@@ -97,13 +107,43 @@ export function generateSemiPrimitiveValue(
97
107
  ): SerovalNode | undefined {
98
108
  if (typeof current === 'bigint') {
99
109
  assert(ctx.features & Feature.BigInt, 'Unsupported type "BigInt"');
100
- return [SerovalNodeType.BigInt, `${current}n`];
110
+ return {
111
+ t: SerovalNodeType.BigInt,
112
+ i: undefined,
113
+ a: undefined,
114
+ s: `${current}n`,
115
+ l: undefined,
116
+ m: undefined,
117
+ c: undefined,
118
+ d: undefined,
119
+ n: undefined,
120
+ };
101
121
  }
102
122
  if (constructorCheck<Date>(current, Date)) {
103
- return [SerovalNodeType.Date, current.toISOString(), id];
123
+ return {
124
+ t: SerovalNodeType.Date,
125
+ i: id,
126
+ a: undefined,
127
+ s: current.toISOString(),
128
+ l: undefined,
129
+ m: undefined,
130
+ c: undefined,
131
+ d: undefined,
132
+ n: undefined,
133
+ };
104
134
  }
105
135
  if (constructorCheck<RegExp>(current, RegExp)) {
106
- return [SerovalNodeType.RegExp, String(current), id];
136
+ return {
137
+ t: SerovalNodeType.RegExp,
138
+ i: id,
139
+ a: undefined,
140
+ s: String(current),
141
+ l: undefined,
142
+ m: undefined,
143
+ c: undefined,
144
+ d: undefined,
145
+ n: undefined,
146
+ };
107
147
  }
108
148
  if (
109
149
  constructorCheck<Int8Array>(current, Int8Array)
@@ -118,7 +158,17 @@ export function generateSemiPrimitiveValue(
118
158
  ) {
119
159
  const constructor = current.constructor.name;
120
160
  assert(ctx.features & Feature.TypedArray, `Unsupported value type "${constructor}"`);
121
- return [SerovalNodeType.TypedArray, [constructor, current.toString(), current.byteOffset], id];
161
+ return {
162
+ t: SerovalNodeType.TypedArray,
163
+ i: id,
164
+ a: undefined,
165
+ s: current.toString(),
166
+ l: current.byteOffset,
167
+ m: undefined,
168
+ c: constructor,
169
+ d: undefined,
170
+ n: undefined,
171
+ };
122
172
  }
123
173
  if (
124
174
  constructorCheck<BigInt64Array>(current, BigInt64Array)
@@ -135,11 +185,17 @@ export function generateSemiPrimitiveValue(
135
185
  result += `${current[i]}n,`;
136
186
  }
137
187
  result += `"${current[cap]}"`;
138
- return [
139
- SerovalNodeType.BigIntTypedArray,
140
- [constructor, result, current.byteOffset],
141
- id,
142
- ];
188
+ return {
189
+ t: SerovalNodeType.TypedArray,
190
+ i: id,
191
+ a: undefined,
192
+ s: result,
193
+ l: current.byteOffset,
194
+ m: undefined,
195
+ c: constructor,
196
+ d: undefined,
197
+ n: undefined,
198
+ };
143
199
  }
144
200
  return undefined;
145
201
  }