ULID Generator — C++ source
Generate Universally Unique Lexicographically Sortable Identifiers (ULID) - 26-character Crockford-base32 strings that sort by millisecond timestamp. Paste any ULID to decode its timestamp and randomness. Runs entirely in your browser.
This is the C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.
// ulid-generator - 26-char Crockford-base32 sortable ID: 10 chars of 48-bit ms timestamp + 16 chars of 80-bit randomness. Language: C++ (C++17, stdlib only). Port of src/lib/ulid.ts (Go twin: cli/ulid-generator) - encode/generate/decode-time core; inspectUlid (date/random-hex view) lives in this dir's javascript.js (80-line budget).
#include <algorithm>
#include <array>
#include <cstdint>
#include <iostream>
#include <random>
#include <stdexcept>
#include <string>
inline const std::string CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // no I, L, O, U
// Divide the big-endian value in b (mutated in place) by 32; return the
// remainder 0-31. Long division in base 256, exactly as in the Go twin.
int div_by_32(std::array<uint8_t, 16>& b) {
int rem = 0;
for (auto& byte : b) {
int cur = (rem << 8) | byte;
byte = static_cast<uint8_t>(cur >> 5);
rem = cur & 0x1F;
}
return rem;
}
// Base32-encode the 128-bit big-endian [6 time bytes | 10 random bytes] into
// the canonical 26 chars. Because 32^16 == 2^80, the first 10 chars equal the
// TS/Go twins' separate time encoding - the cross-language lock-step anchor.
std::string encode_ulid(const std::array<uint8_t, 6>& time_b, const std::array<uint8_t, 10>& rnd_b) {
std::array<uint8_t, 16> b;
std::copy(time_b.begin(), time_b.end(), b.begin());
std::copy(rnd_b.begin(), rnd_b.end(), b.begin() + 6);
std::string out(26, ' ');
for (int i = 25; i >= 0; --i) out[i] = CROCKFORD[div_by_32(b)];
return out;
}
// 48-bit ms timestamp -> 6 big-endian bytes (MSB first), like TS timeBytes().
std::array<uint8_t, 6> time_bytes(uint64_t ms) {
std::array<uint8_t, 6> out{};
for (int i = 5; i >= 0; --i, ms >>= 8) out[i] = ms & 0xFF;
return out;
}
// New ULID for the epoch-ms. std::random_device is the OS entropy source -
// the C++ analogue of Go's crypto/rand.
std::string generate_ulid(uint64_t ms) {
std::array<uint8_t, 10> rnd{};
for (auto& byte : rnd) byte = static_cast<uint8_t>(std::random_device{}());
return encode_ulid(time_bytes(ms), rnd);
}
// Extract the 48-bit ms timestamp from the first 10 chars; throws on
// malformed input (wrong length, or a char outside the alphabet such as I/L/O/U).
uint64_t decode_ulid_time(const std::string& id) {
if (id.size() != 26) throw std::invalid_argument("ulid: malformed id: want 26 chars");
uint64_t ts = 0;
for (size_t i = 0; i < 10; ++i) {
size_t v = CROCKFORD.find(id[i]);
if (v == std::string::npos)
throw std::invalid_argument(std::string("ulid: invalid character ") + id[i]);
ts = ts * 32 + v;
}
return ts;
}
int main() {
const std::string ulid = generate_ulid(1722000000000ULL);
std::cout << "ulid: " << ulid << '\n';
std::cout << "time(ms): " << decode_ulid_time(ulid) << " (round-trip ok)\n";
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →