All files / util/src/json-size json.ts

98.33% Statements 59/60
94.28% Branches 33/35
100% Functions 7/7
98% Lines 49/50

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 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 992x   2x 65577x 65577x 52985x     2x 127038x 127038x 127038x 127038x 1053563x 1053563x 1047780x               23864x 23864x     1047780x 5783x   121255x     13622x   2x 13006x 13006x 87303x 13006x     2x 11887x 11887x 11887x 74321x       74321x 74321x   11887x 11887x 11887x                 2x 161753x 156794x   65572x   52717x   13620x   24885x 11884x                   2x 16x 15x   5x       2x   8x 3x    
import {utf8Size} from '../strings/utf8';
 
const numberSize = (num: number) => {
  const isInteger = num === Math.round(num);
  if (isInteger) return Math.max(Math.floor(Math.log10(Math.abs(num))), 0) + 1 + (num < 0 ? 1 : 0);
  return JSON.stringify(num).length;
};
 
const stringSize = (str: string) => {
  const strLength = str.length;
  let byteLength = strLength;
  let pos = 0;
  while (pos < strLength) {
    const value = str.charCodeAt(pos++);
    if (value < 128) {
      switch (value) {
        case 8: // \b
        case 9: // \t
        case 10: // \n
        case 12: // \f
        case 13: // \r
        case 34: // \"
        case 92: // \\
          byteLength += 1;
          break;
      }
      // biome-ignore lint: keep this continue
      continue;
    } else return utf8Size(JSON.stringify(str));
  }
  return byteLength + 2;
};
 
const booleanSize = (bool: boolean) => (bool ? 4 : 5);
 
const arraySize = (arr: unknown[]) => {
  let size = 0;
  const length = arr.length;
  for (let i = 0; i < length; i++) size += jsonSize(arr[i]);
  return size + 2 + (length > 1 ? length - 1 : 0);
};
 
const objectSize = (obj: Record<string, unknown>) => {
  let size = 2;
  let length = 0;
  for (const key in obj)
    Eif (
      // biome-ignore lint: .hasOwnProperty access is intentional
      obj.hasOwnProperty(key)
    ) {
      length++;
      size += stringSize(key) + jsonSize(obj[key]);
    }
  const colonSize = length;
  const commaSize = length > 1 ? length - 1 : 0;
  return size + colonSize + commaSize;
};
 
/**
 * Computes exact prices JSON size as would be output from JSON.stringify().
 *
 * @param value JSON value to approximate size of
 * @returns Size in bytes of JSON value
 */
export const jsonSize = (value: unknown): number => {
  if (value === null) return 4;
  switch (typeof value) {
    case 'number':
      return numberSize(value);
    case 'string':
      return stringSize(value);
    case 'boolean':
      return booleanSize(value);
  }
  if (value instanceof Array) return arraySize(value);
  return objectSize(value as Record<string, unknown>);
};
 
/**
 * Same as `jsonSize` function, but approximates the size of strings to improve performance.
 * Uses `.length` property of strings to approximate their size.
 *
 * @param value JSON value to approximate size of
 * @returns Size in bytes of JSON value
 */
export const jsonSizeApprox = (value: unknown): number => {
  if (value === null) return 4;
  switch (typeof value) {
    case 'number':
      return numberSize(value);
    case 'string':
      return value.length;
    case 'boolean':
      return booleanSize(value);
  }
  if (value instanceof Array) return arraySize(value);
  return objectSize(value as Record<string, unknown>);
};