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

85.18% Statements 23/27
66.66% Branches 8/12
100% Functions 1/1
82.6% Lines 19/23

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                              2x 91x   5x   62x   2x   22x   21x 21x   4x 4x 4x 12x 4x           17x 17x 17x 33x       33x 17x                        
export const enum MaxEncodingOverhead {
  Null = 4, // Literal "null"
  Boolean = 5, // Literal "false"
  Number = 22, // Literal "1.1111111111111111e+21" = JSON.stringify(1111111111111111111112)
  String = 1 + 4, // As per TLV: 1 byte for type, 4 bytes for length.
  StringLengthMultiplier = 5, // 4x UTF-8 overhead + 1.3x Base64 overhead, plus, 1 byte for each non-ASCII character.
  Binary = 2 + 37 + 2, // 2 for two quotes, 37 for "data:application/octet-stream;base64,'" literal, 2 bytes for Base64 padding.
  BinaryLengthMultiplier = 2, // 1.3x Base64 overhead.
  Array = 1 + 4, // As per TLV: 1 byte for type, 4 bytes for length.
  ArrayElement = 1, // Separator "," literal.
  Object = 1 + 4, // As per TLV: 1 byte for type, 4 bytes for length.
  ObjectElement = 1 + 1, // 1 byte for Key-value separator ":" literal, and 1 byte for separator "," literal.
  Undefined = Binary + BinaryLengthMultiplier * 2,
}
 
export const maxEncodingCapacity = (value: unknown): number => {
  switch (typeof value) {
    case 'number':
      return MaxEncodingOverhead.Number;
    case 'string':
      return MaxEncodingOverhead.String + value.length * MaxEncodingOverhead.StringLengthMultiplier;
    case 'boolean':
      return MaxEncodingOverhead.Boolean;
    case 'object': {
      if (!value) return MaxEncodingOverhead.Null;
      // biome-ignore lint: fine name
      const constructor = value.constructor;
      switch (constructor) {
        case Array: {
          const arr = value as unknown[];
          const length = arr.length;
          let size = MaxEncodingOverhead.Array + length * MaxEncodingOverhead.ArrayElement;
          for (let i = arr.length - 1; i >= 0; i--) size += maxEncodingCapacity(arr[i]);
          return size;
        }
        case Uint8Array: {
          return MaxEncodingOverhead.Binary + (value as Uint8Array).length * MaxEncodingOverhead.BinaryLengthMultiplier;
        }
        case Object: {
          let size = MaxEncodingOverhead.Object;
          const obj = value as Record<string, unknown>;
          for (const key in obj)
            if (
              // biome-ignore lint: .hasOwnProperty access is intentional
              obj.hasOwnProperty(key)
            )
              size += MaxEncodingOverhead.ObjectElement + maxEncodingCapacity(key) + maxEncodingCapacity(obj[key]);
          return size;
        }
        default:
          return MaxEncodingOverhead.Undefined;
      }
    }
    case 'bigint':
      return MaxEncodingOverhead.Number;
    default:
      return MaxEncodingOverhead.Undefined;
  }
};