All files / utils BitUtils.ts

100% Statements 47/47
97.95% Branches 48/49
100% Functions 11/11
100% Lines 44/44

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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236                              48x 17x 10x                         90x 90x 90x 82x 82x     28x 90x                         94x 17x   5x   6x   2x   2x   2x     77x   40x   7x   6x   2x   22x                       11x                     20x                       6x 2x   4x                                 7x 7x 4x   3x                                       15x 15x 15x   15x     15x                                         16x 16x                                       5x     5x 5x                                           6x 6x          
/**
 * Bit manipulation utilities for C code generation.
 * Pure functions that generate C code strings for bit operations.
 *
 * Extracted from CodeGenerator.ts as part of ADR-109 decomposition.
 */
class BitUtils {
  /**
   * Convert a boolean expression to an integer (0 or 1).
   * Handles literal "true"/"false" and generates ternary for expressions.
   *
   * @param expr - The expression to convert
   * @returns C code string representing the integer value
   */
  static boolToInt(expr: string): string {
    if (expr === "true") return "1";
    if (expr === "false") return "0";
    return `(${expr} ? 1 : 0)`;
  }
 
  /**
   * Generate a bit mask for the given width.
   * Uses pre-computed hex values for common widths to avoid undefined behavior.
   *
   * @param width - The bit width (number or string expression)
   * @param targetType - Optional target type for 64-bit aware mask generation
   * @returns C code string for the mask
   */
  static generateMask(width: string | number, targetType?: string): string {
    const widthNum =
      typeof width === "number" ? width : Number.parseInt(width, 10);
    const is64Bit = targetType === "u64" || targetType === "i64";
    if (!Number.isNaN(widthNum)) {
      const hex = BitUtils.maskHex(widthNum, is64Bit);
      if (hex) return hex;
    }
    // Use ULL for 64-bit types to avoid overflow on large shifts
    const one = is64Bit ? "1ULL" : "1U";
    return `((${one} << ${width}) - 1)`;
  }
 
  /**
   * Return pre-computed hex mask for common bit widths.
   * Returns null for uncommon widths.
   *
   * @param width - The bit width
   * @param is64Bit - Whether the target is a 64-bit type (use ULL suffix)
   * @returns Hex mask string or null
   */
  static maskHex(width: number, is64Bit = false): string | null {
    // For 64-bit targets, use ULL suffix to prevent overflow on shifts >= 32
    if (is64Bit) {
      switch (width) {
        case 8:
          return "0xFFULL";
        case 16:
          return "0xFFFFULL";
        case 32:
          return "0xFFFFFFFFULL";
        case 64:
          return "0xFFFFFFFFFFFFFFFFULL";
        default:
          return null;
      }
    }
    switch (width) {
      case 8:
        return "0xFFU";
      case 16:
        return "0xFFFFU";
      case 32:
        return "0xFFFFFFFFU";
      case 64:
        return "0xFFFFFFFFFFFFFFFFULL";
      default:
        return null;
    }
  }
 
  /**
   * Return the appropriate "1" literal for a given type.
   * Uses "1ULL" for 64-bit types to avoid undefined behavior on large shifts.
   *
   * @param typeName - The C-Next type name (e.g., "u64", "i32")
   * @returns "1ULL" for 64-bit types, "1" otherwise
   */
  static oneForType(typeName: string): string {
    return typeName === "u64" || typeName === "i64" ? "1ULL" : "1";
  }
 
  /**
   * Format a number as an uppercase hex string (e.g., 255 -> "0xFF").
   * Used for generating hex mask literals in generated C code.
   *
   * @param value - The numeric value to format
   * @returns Hex string like "0xFF" or "0x1F"
   */
  static formatHex(value: number): string {
    return `0x${value.toString(16).toUpperCase()}`;
  }
 
  /**
   * Generate code to read a single bit from a value.
   * Pattern: ((target >> offset) & 1)
   *
   * @param target - The value to read from
   * @param offset - Bit position (0-indexed)
   * @returns C code string for the bit read
   */
  static singleBitRead(target: string, offset: string | number): string {
    if (offset === 0 || offset === "0") {
      return `((${target}) & 1)`;
    }
    return `((${target} >> ${offset}) & 1)`;
  }
 
  /**
   * Generate code to read multiple bits from a value.
   * Pattern: ((target >> offset) & mask)
   *
   * @param target - The value to read from
   * @param offset - Starting bit position (0-indexed)
   * @param width - Number of bits to read
   * @returns C code string for the bit range read
   */
  static bitRangeRead(
    target: string,
    offset: string | number,
    width: string | number,
  ): string {
    const mask = BitUtils.generateMask(width);
    if (offset === 0 || offset === "0") {
      return `((${target}) & ${mask})`;
    }
    return `((${target} >> ${offset}) & ${mask})`;
  }
 
  /**
   * Generate read-modify-write code for single bit assignment.
   * Pattern: target = (target & ~(1 << offset)) | (value << offset)
   * Converts boolean values via boolToInt.
   *
   * @param target - The variable to modify
   * @param offset - Bit position (0-indexed)
   * @param value - Value to write (will be converted via boolToInt)
   * @param targetType - Optional target type for 64-bit aware code generation
   * @returns C code string for the assignment
   */
  static singleBitWrite(
    target: string,
    offset: string | number,
    value: string,
    targetType?: string,
  ): string {
    const intValue = BitUtils.boolToInt(value);
    const is64Bit = targetType === "u64" || targetType === "i64";
    const one = is64Bit ? "1ULL" : "1";
    // For 64-bit types, cast the value to ensure shift doesn't overflow
    const valueShift = is64Bit
      ? `((uint64_t)${intValue} << ${offset})`
      : `(${intValue} << ${offset})`;
    return `${target} = (${target} & ~(${one} << ${offset})) | ${valueShift};`;
  }
 
  /**
   * Generate read-modify-write code for multi-bit assignment.
   * Pattern: target = (target & ~(mask << offset)) | ((value & mask) << offset)
   *
   * @param target - The variable to modify
   * @param offset - Starting bit position (0-indexed)
   * @param width - Number of bits to write
   * @param value - Value to write
   * @param targetType - Optional target type for 64-bit aware code generation
   * @returns C code string for the assignment
   */
  static multiBitWrite(
    target: string,
    offset: string | number,
    width: string | number,
    value: string,
    targetType?: string,
  ): string {
    const mask = BitUtils.generateMask(width, targetType);
    return `${target} = (${target} & ~(${mask} << ${offset})) | ((${value} & ${mask}) << ${offset});`;
  }
 
  /**
   * Generate write-only register code for single bit assignment.
   * No read-modify-write, just shifts the value into position.
   * Pattern: target = (value << offset)
   *
   * @param target - The register to write
   * @param offset - Bit position (0-indexed)
   * @param value - Value to write (will be converted via boolToInt)
   * @param targetType - Optional target type for 64-bit aware code generation
   * @returns C code string for the assignment
   */
  static writeOnlySingleBit(
    target: string,
    offset: string | number,
    value: string,
    targetType?: string,
  ): string {
    const intValue = BitUtils.boolToInt(value);
    // For 64-bit types, cast to ensure correct shift width
    const castPrefix =
      targetType === "u64" || targetType === "i64" ? "(uint64_t)" : "";
    return `${target} = (${castPrefix}${intValue} << ${offset});`;
  }
 
  /**
   * Generate write-only register code for multi-bit assignment.
   * No read-modify-write, just shifts the masked value into position.
   * Pattern: target = ((value & mask) << offset)
   *
   * @param target - The register to write
   * @param offset - Starting bit position (0-indexed)
   * @param width - Number of bits to write
   * @param value - Value to write
   * @param targetType - Optional target type for 64-bit aware code generation
   * @returns C code string for the assignment
   */
  static writeOnlyMultiBit(
    target: string,
    offset: string | number,
    width: string | number,
    value: string,
    targetType?: string,
  ): string {
    const mask = BitUtils.generateMask(width, targetType);
    return `${target} = ((${value} & ${mask}) << ${offset});`;
  }
}
 
export default BitUtils;