summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/common/internal/query/json_param_value.ts
blob: f4be7642d3305a3b16c9896dd01ead73b5c054a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { assert, sortObjectByKey } from '../../util/util.js';
import { JSONWithUndefined } from '../params_utils.js';

// JSON can't represent various values and by default stores them as `null`.
// Instead, storing them as a magic string values in JSON.
const jsUndefinedMagicValue = '_undef_';
const jsNaNMagicValue = '_nan_';
const jsPositiveInfinityMagicValue = '_posinfinity_';
const jsNegativeInfinityMagicValue = '_neginfinity_';

// -0 needs to be handled separately, because -0 === +0 returns true. Not
// special casing +0/0, since it behaves intuitively. Assuming that if -0 is
// being used, the differentiation from +0 is desired.
const jsNegativeZeroMagicValue = '_negzero_';

const toStringMagicValue = new Map<unknown, string>([
  [undefined, jsUndefinedMagicValue],
  [NaN, jsNaNMagicValue],
  [Number.POSITIVE_INFINITY, jsPositiveInfinityMagicValue],
  [Number.NEGATIVE_INFINITY, jsNegativeInfinityMagicValue],
  // No -0 handling because it is special cased.
]);

const fromStringMagicValue = new Map<string, unknown>([
  [jsUndefinedMagicValue, undefined],
  [jsNaNMagicValue, NaN],
  [jsPositiveInfinityMagicValue, Number.POSITIVE_INFINITY],
  [jsNegativeInfinityMagicValue, Number.NEGATIVE_INFINITY],
  // -0 is handled in this direction because there is no comparison issue.
  [jsNegativeZeroMagicValue, -0],
]);

function stringifyFilter(k: string, v: unknown): unknown {
  // Make sure no one actually uses a magic value as a parameter.
  if (typeof v === 'string') {
    assert(
      !fromStringMagicValue.has(v),
      `${v} is a magic value for stringification, so cannot be used`
    );

    assert(
      v !== jsNegativeZeroMagicValue,
      `${v} is a magic value for stringification, so cannot be used`
    );
  }

  if (Object.is(v, -0)) {
    return jsNegativeZeroMagicValue;
  }

  return toStringMagicValue.has(v) ? toStringMagicValue.get(v) : v;
}

export function stringifyParamValue(value: JSONWithUndefined): string {
  return JSON.stringify(value, stringifyFilter);
}

/**
 * Like stringifyParamValue but sorts dictionaries by key, for hashing.
 */
export function stringifyParamValueUniquely(value: JSONWithUndefined): string {
  return JSON.stringify(value, (k, v) => {
    if (typeof v === 'object' && v !== null) {
      return sortObjectByKey(v);
    }

    return stringifyFilter(k, v);
  });
}

// 'any' is part of the JSON.parse reviver interface, so cannot be avoided.
// eslint-disable-next-line  @typescript-eslint/no-explicit-any
function parseParamValueReviver(k: string, v: any): any {
  if (fromStringMagicValue.has(v)) {
    return fromStringMagicValue.get(v);
  }

  return v;
}

export function parseParamValue(s: string): JSONWithUndefined {
  return JSON.parse(s, parseParamValueReviver);
}