| 163 | // Format an integer value |
| 164 | template <typename T> |
| 165 | fl::string format_integer(T value, const FormatSpec& spec) { |
| 166 | char buf[68]; // Enough for 64-bit binary + prefix |
| 167 | char* p = buf + sizeof(buf); |
| 168 | *--p = '\0'; |
| 169 | |
| 170 | bool negative = false; |
| 171 | using UnsignedT = typename fl::make_unsigned<T>::type; |
| 172 | UnsignedT uval; |
| 173 | |
| 174 | if (fl::is_signed<T>::value && value < 0) { |
| 175 | negative = true; |
| 176 | uval = static_cast<UnsignedT>(-(value + 1)) + 1; // Safe negation |
| 177 | } else { |
| 178 | uval = static_cast<UnsignedT>(value); |
| 179 | } |
| 180 | |
| 181 | char type = spec.type ? spec.type : 'd'; |
| 182 | int base = 10; |
| 183 | const char* digits = "0123456789abcdef"; |
| 184 | |
| 185 | switch (type) { |
| 186 | case 'b': case 'B': base = 2; break; |
| 187 | case 'o': base = 8; break; |
| 188 | case 'x': base = 16; digits = "0123456789abcdef"; break; |
| 189 | case 'X': base = 16; digits = "0123456789ABCDEF"; break; |
| 190 | case 'c': { |
| 191 | // Character output |
| 192 | char ch = static_cast<char>(value); |
| 193 | return fl::string(&ch, 1); |
| 194 | } |
| 195 | default: base = 10; break; |
| 196 | } |
| 197 | |
| 198 | // Convert to string (in reverse) |
| 199 | if (uval == 0) { |
| 200 | *--p = '0'; |
| 201 | } else { |
| 202 | while (uval > 0) { |
| 203 | *--p = digits[uval % base]; |
| 204 | uval /= base; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | // Build prefix |
| 209 | fl::string prefix; |
| 210 | if (negative) { |
| 211 | prefix.append('-'); |
| 212 | } else if (spec.sign == '+') { |
| 213 | prefix.append('+'); |
| 214 | } else if (spec.sign == ' ') { |
| 215 | prefix.append(' '); |
| 216 | } |
| 217 | |
| 218 | if (spec.alternate && base != 10) { |
| 219 | if (base == 16) { |
| 220 | prefix.append('0'); |
| 221 | prefix.append('x'); // Always lowercase 'x' in prefix (digits control case) |
| 222 | } else if (base == 2) { |
no test coverage detected