Implementation:ClickHouse ClickHouse Itoa
| Knowledge Sources | |
|---|---|
| Domains | Formatting, Performance |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
Highly optimized integer-to-string conversion functions based on the jeaiii algorithm, providing faster formatting than standard library alternatives.
Description
This code implements fast integer-to-ASCII conversion for all integer types from 8-bit to 256-bit, including signed and unsigned variants. The implementation is based on James Edward Anhalt III's (jeaiii) algorithm, which uses fixed-point arithmetic and lookup tables to avoid expensive division operations. The key optimization is processing two digits at a time using pre-computed tables and carefully tuned arithmetic to determine digit values without division. For very large integers (128-bit and 256-bit), the code uses specialized algorithms that break numbers into manageable chunks. The implementation includes custom optimizations for small numbers (1-2 digits) where branch prediction makes direct handling faster.
Usage
Use this when you need to format integers to strings in performance-critical code, such as logging, JSON generation, CSV export, or any scenario where integer formatting is a bottleneck. The jeaiii algorithm is particularly effective for database workloads where small-to-medium integers are common.
Code Reference
Source Location
- Repository: ClickHouse
- File: base/base/itoa.cpp
- Lines: 1-498
Signature
// Integer to string conversion
char * itoa(UInt8 i, char * p);
char * itoa(Int8 i, char * p);
char * itoa(UInt16 i, char * p);
char * itoa(Int16 i, char * p);
char * itoa(UInt32 i, char * p);
char * itoa(Int32 i, char * p);
char * itoa(UInt64 i, char * p);
char * itoa(Int64 i, char * p);
char * itoa(UInt128 i, char * p);
char * itoa(Int128 i, char * p);
char * itoa(UInt256 i, char * p);
char * itoa(Int256 i, char * p);
// Internal helper functions
namespace jeaiii {
template <class T>
inline ALWAYS_INLINE char * to_text_from_integer(char * b, T i);
}
ALWAYS_INLINE inline char * outOneDigit(char * p, uint8_t value);
ALWAYS_INLINE inline char * outTwoDigits(char * p, uint8_t value);
Import
#include <base/itoa.h>
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| i | Integer type (Int8-Int256, UInt8-UInt256) | Yes | Integer value to convert |
| p | char * | Yes | Output buffer (must have sufficient space) |
Outputs
| Name | Type | Description |
|---|---|---|
| end_ptr | char * | Pointer to position after last written character |
Usage Examples
#include <base/itoa.h>
#include <iostream>
// Convert 32-bit integer
char buffer[32];
UInt32 value = 12345;
char * end = itoa(value, buffer);
*end = '\0'; // Null-terminate
std::cout << buffer << std::endl; // "12345"
// Convert negative integer
Int64 negative = -9876543210;
char neg_buffer[32];
char * neg_end = itoa(negative, neg_buffer);
*neg_end = '\0';
std::cout << neg_buffer << std::endl; // "-9876543210"
// Build string with multiple integers
char output[100];
char * pos = output;
*pos++ = '[';
pos = itoa(UInt32(1), pos);
*pos++ = ',';
*pos++ = ' ';
pos = itoa(UInt32(2), pos);
*pos++ = ',';
*pos++ = ' ';
pos = itoa(UInt32(3), pos);
*pos++ = ']';
*pos = '\0';
std::cout << output << std::endl; // "[1, 2, 3]"
// Format very large integers (128-bit)
UInt128 large = UInt128{0, 1}; // 2^64
char large_buffer[64];
char * large_end = itoa(large, large_buffer);
*large_end = '\0';
std::cout << large_buffer << std::endl; // "18446744073709551616"
// Format 256-bit integers
UInt256 huge = UInt256{0, 0, 0, 1}; // 2^192
char huge_buffer[128];
char * huge_end = itoa(huge, huge_buffer);
*huge_end = '\0';
std::cout << huge_buffer << std::endl;
// Fast formatting in tight loop
std::vector<UInt64> numbers = {1, 42, 123, 9999, 1000000};
char loop_buffer[32];
for (UInt64 num : numbers) {
char * end_pos = itoa(num, loop_buffer);
size_t length = end_pos - loop_buffer;
// Process formatted string
std::string_view formatted(loop_buffer, length);
}
// Format with known buffer size
UInt32 small_value = 99;
char small_buf[16]; // More than enough for UInt32
char * result = itoa(small_value, small_buf);
std::string formatted(small_buf, result - small_buf); // "99"