All files / json-hash structHash.ts

96.55% Statements 28/29
92.3% Branches 12/13
100% Functions 1/1
95.83% Lines 23/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 48173x 173x                         173x 55645x   12222x     27450x   5120x   10853x 8461x 3713x 3713x 9437x 3713x 4748x 22x   4726x 4726x 4726x 4726x 4726x 20943x 20943x   4726x            
import {sort} from '@jsonjoy.com/util/lib/sort/insertion';
import {hash} from './hash';
 
/**
 * Produces a *structural hash* of a JSON value.
 *
 * This is a hash that is not sensitive to the order of properties in object and
 * it preserves spatial information of the JSON nodes.
 *
 * The hash is guaranteed to contain only printable ASCII characters, excluding
 * the newline character.
 *
 * @param val A JSON value to hash.
 */
export const structHash = (val: unknown): string => {
  switch (typeof val) {
    case 'string':
      return hash(val).toString(36);
    case 'number':
    case 'bigint':
      return val.toString(36);
    case 'boolean':
      return val ? 'T' : 'F';
    case 'object':
      if (val === null) return 'N';
      if (Array.isArray(val)) {
        const length = val.length;
        let res = '[';
        for (let i = 0; i < length; i++) res += structHash(val[i]) + ',';
        return res + ']';
      } else if (val instanceof Uint8Array) {
        return hash(val).toString(36);
      } else {
        const keys = Object.keys(val);
        sort(keys);
        let res = '{';
        const length = keys.length;
        for (let i = 0; i < length; i++) {
          const key = keys[i];
          res += hash(key).toString(36) + ':' + structHash((val as Record<string, unknown>)[key]) + ',';
        }
        return res + '}';
      }
    default:
      return 'U';
  }
};