All files / utils LengthProperty.ts

95.65% Statements 22/23
92.59% Branches 25/27
100% Functions 5/5
95.23% Lines 20/21

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                              114x   114x                 126x                           39x 14x 14x   25x 23x 23x 16x 11x   18x 18x         6x                 17x   9x   1x       1x   6x                 8x                    
/**
 * ADR-058's compile-time length properties -- `.element_count`, `.bit_length`,
 * `.byte_length` -- decided once (#1175).
 *
 * Render writes them into expressions and the constant evaluator folds them
 * into dimensions; both ask here, so `u8[src.element_count]` sizes an array
 * by the number `src.element_count` reads as. Render alone adds a C header
 * type's width, from a table 1.4 may not read; for one of those a property is
 * not a constant.
 */
import TYPE_WIDTH from "../types/TYPE_WIDTH";
import type IElementWidthFacts from "./types/IElementWidthFacts";
import type TType from "../types/TType";
 
/** ADR-017: an enum is 32 bits wide */
const ENUM_BITS = 32;
 
const LENGTHS: ReadonlySet<string> = new Set([
  "element_count",
  "bit_length",
  "byte_length",
]);
 
class LengthProperty {
  /** Whether a member name is one of ADR-058's compile-time length properties */
  static isLength(property: string): boolean {
    return LENGTHS.has(property);
  }
 
  /**
   * The property's value for a value with `dimensions` whose element is
   * `elementBits` wide; null when a dimension or the width is not known here.
   * `.element_count` is the first dimension; the lengths are every element's
   * bits together, and `.byte_length` is `.bit_length / 8`.
   */
  static of(
    property: string,
    dimensions: ReadonlyArray<number | string>,
    elementBits: number | null,
  ): number | null {
    if (property === "element_count") {
      const first = dimensions[0];
      return typeof first === "number" && first > 0 ? first : null;
    }
    if (elementBits === null || elementBits <= 0) return null;
    let elements = 1;
    for (const dimension of dimensions) {
      if (typeof dimension !== "number" || dimension <= 0) return null;
      elements *= dimension;
    }
    const bits = elements * elementBits;
    return property === "bit_length" ? bits : bits / 8;
  }
 
  /** ADR-058: a string's element is its whole buffer, `.size` bytes */
  static stringElementBits(capacity: number): number {
    return (capacity + 1) * 8;
  }
 
  /**
   * A C-Next declared type's element width in bits -- the same answer
   * `elementBits` gives render for the type's name; null for a type whose
   * width C-Next does not fix (a struct, a C header type)
   */
  static elementBitsOfType(type: TType): number | null {
    switch (type.kind) {
      case "primitive":
        return TYPE_WIDTH[type.primitive] ?? null;
      case "enum":
        return ENUM_BITS;
      case "bitmap":
        return type.bitWidth;
      case "string":
        return LengthProperty.stringElementBits(type.capacity);
      default:
        return null;
    }
  }
 
  /** An element type's width in bits, from C-Next's own types; null if unknown */
  static elementBits(
    typeName: string,
    facts: IElementWidthFacts,
  ): number | null {
    return (
      TYPE_WIDTH[typeName] ??
      facts.enumBitWidth(typeName) ??
      (facts.isEnum(typeName) ? ENUM_BITS : null) ??
      facts.bitmapBitWidth(typeName)
    );
  }
}
 
export default LengthProperty;