2026-06-14 16:00:45
諏訪子
c
zig
go
low-level

【C言語/Go/Zig】ゼロからUUIDジェネレーターを実装

今日は3つのプログラミング言語(C言語、Go、Zig)でUUIDジェネレーターを一緒に実装したいと思います。
理由はシンプルです:既存のUUIDジェネレーターの多くが無駄に大き過ぎるからです。
オプションが多過ぎる物が多く、あたしは只「ツールを実行して直ぐにUUIDを得る」だけが欲しいのです。

UUIDとは?

UUIDはインターネット上で益々見かける様になっている長い識別子文字列です。
128ビットの長さで、32桁の16進数文字をハイフンで区切った形式です:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

UUIDには複数のバージョンがあります:

  • バージョン1:時間ベース(グレゴリオ暦)、時間順、一般用途、MACアドレス+タイムスタンプに基づいて生成。
  • バージョン2:DCE Security UUID用で、標準で規定されていない為省略される事が多い。
  • バージョン3:名前ベース、決定論的、内容由来、MD5ハッシュに基づいて生成。
  • バージョン4:完全にランダム、一般用途、ランダムバイトから生成。
  • バージョン5:名前ベース、決定論的、内容由来、SHA1ハッシュに基づいて生成。
  • バージョン6:時間ベース(グレゴリオ暦)、時間順、より良い時間ソート用、バージョン1を並べ替えた物。
  • バージョン7:時間ベース(Unix Epoch)、時間順、現代のアプリケーション向け、Unixタイムスタンプに基づく。
  • バージョン8:現在はベンダー固有のユースケース向けに実験中。

バージョン9〜15は将来の定義のために予約済み。バージョン0は未使用。

バージョン7が現代的とされていますが、バージョン4はランダム性において最適です。その為、あたしはバージョン4のみを実装します。

バージョン4では、バージョンとバリアントの2桁だけを固定する必要があります。残りは完全にランダムに出来ます。

バリアントフィールドについて:

  • 1〜7:NCS(Network Computing System)後方互換用で、Nil UUIDを含む。
  • CとD:Microsoft後方互換用。
  • EとF:将来の定義用で、Max UUIDを含む。

従って、残るのは8、9、A、Bのみです。

C言語

#include <stdio.h>
#include <stdint.h>

#if defined(_WIN64)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <bcrypt.h>
#endif

int main(void) {
  uint8_t data[16];

#if defined(_WIN64)
  NTSTATUS n = BCryptGenRandom(NULL, data, sizeof(data),
               BCRYPT_USE_SYSTEM_PREFERRED_RNG);
#else
  FILE *fp = fopen("/dev/urandom", "rb");
  fread(data, 1, sizeof(data), fp);
  fclose(fp);
#endif

  data[6] = (data[6] & 0x0F) | 0x40;
  data[8] = (data[8] & 0x3F) | 0x80;
  char out[37];
  snprintf(out, 37,
    "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
    data[0], data[1], data[2], data[3],
    data[4], data[5],
    data[6], data[7],
    data[8], data[9],
    data[10], data[11], data[12], data[13], data[14], data[15]);
  printf("%s\n", out);

  return 0;
}

BSD、Linux、macOS、Illumos

$ cc -O3 uuid.c -o uuid -static
$ strip uuid
$ ls -thal uuid
-rwxr-xr-x  1 suwako suwako  691K  6月 14 15:05 uuid
$ ./uuid
dd9a18ea-6485-4827-95fe-34a414e54f61
$ ./uuid
6a755d71-e2c3-4908-9b5b-1b1cac9f1dbf
$ ./uuid
d18d8d7c-ed9a-4a43-b726-40aa4e2ac005
$ ./uuid
3514aab1-68b6-4df5-ae0a-3bbaba572262
$ ./uuid
54da7079-a5a9-48b7-ac04-40a9d58d1ac0
$ ./uuid
a16ba8a1-567d-4d1c-acb0-7cfd7da1ed85
$ ./uuid
3296c8da-8a27-4afb-b53b-0805f5aca55a
$ ./uuid
6ca79551-131c-400b-8aa5-65176a26038a
$ ./uuid
56d059b9-9536-4b20-9df6-7a75d637410b
$ ./uuid
55b1648e-590d-4ecc-a505-a7b21a4dd52e

Windows

> cl.exe uuid.c /link bcrypt.lib /out:uuid.exe
Microsoft (R) C/C++ Optimizing Compiler Version 19.43.34810 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

uuid.c
Microsoft (R) Incremental Linker Version 14.43.34810.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:uuid.exe
bcrypt.lib
/out:uuid.exe
uuid.obj
> .\uuid.exe
a0a0bc9e-9243-46e5-82f4-95b48c4baa78
> .\uuid.exe
6f6c839f-fc3b-47ef-8c8e-5fd0554793b2
> .\uuid.exe
07762170-dcdc-4e49-b127-e87c01462487
> .\uuid.exe
af35e21e-b030-4504-9ad3-99dfefdf373f
> .\uuid.exe
5f105a97-e6f7-483a-9058-963d56af3cc8
> .\uuid.exe
032763af-7db9-468b-be58-58fea4711d59
> .\uuid.exe
89205d97-e78d-4b32-b113-4c9f909f6149
> .\uuid.exe
79206b73-6c7e-43ba-81d1-2bc316ff3641
> .\uuid.exe
675150e6-983b-4018-83d7-d3444ec7cbd1
> .\uuid.exe
a9a0d60c-f912-4a6b-9d01-db3e290e14a9

Go

package main

import (
  "crypto/rand"
  "fmt"
)

func main() {
  data := make([]byte, 16)
  _, err := rand.Read(data)
  if err != nil {
    fmt.Println(err)
    return
  }

  data[6] = (data[6] & 0x0F) | 0x40
  data[8] = (data[8] & 0x3F) | 0x80
  fmt.Printf(
    "%x-%x-%x-%x-%x\n",
    data[0:4],
    data[4:6],
    data[6:8],
    data[8:10],
    data[10:16],
  )
}

$ go build -ldflags "-w -s" uuid.go
$ strip uuid
$ ls -thal uuid
-rwxr-xr-x  1 suwako suwako  1.7M  6月 14 15:09 uuid
$ ./uuid
d3ba3a2a-5378-4eb5-b1d2-b11f1bb37114
$ ./uuid
39b3ef52-1042-407d-8184-30fb9b7db00e
$ ./uuid
fd801a2c-c797-4568-a189-c0462ceb86cf
$ ./uuid
93d39072-169f-4513-8004-ab160526ab88
$ ./uuid
9e5ef1c0-9256-41ec-a4d8-faea557d8c10
$ ./uuid
62e26a1e-1796-451e-9c44-af34ed37526f
$ ./uuid
d71ed1a1-8518-42e8-b935-a7299ebb966c
$ ./uuid
a6143ba7-431f-475a-babc-b60a45e9ab45

Zig

const std = @import("std");

pub fn main(init: std.process.Init) !void {
  var data: [16]u8 = undefined;
  init.io.random(&data);
  data[6] = (data[6] & 0x0F) | 0x40;
  data[8] = (data[8] & 0x3F) | 0x80;

  const hex = std.fmt.bytesToHex(&data, .lower);
  const id = try std.fmt.allocPrint(init.gpa, "{s}-{s}-{s}-{s}-{s}", .{
    hex[0..8],
    hex[8..12],
    hex[12..16],
    hex[16..20],
    hex[20..32],
  });
  defer init.gpa.free(id);
  std.debug.print("{s}\n", .{ id });
}

$ zig build-exe uuid.zig -Doptimize=ReleaseSmall -static
$ strip uuid
$ ls -thal uuid
-rwxr-xr-x  1 suwako suwako  2.8M  6月 14 15:07 uuid
$ ./uuid
eb86adcf-2368-4413-b75e-373cc972a1df
$ ./uuid
77b2aec4-3ae5-43a4-aa2b-3945dc261300
$ ./uuid
5dac8e99-2412-4aa4-8ec6-64ee0a3cf52f
$ ./uuid
6794f31b-c946-409a-8fa0-2461573978cf
$ ./uuid
a7c78aaf-2fc8-4043-a891-8964e1522d3a
$ ./uuid
d9468118-8a63-4cec-98e0-aea278a7fdf3
$ ./uuid
8cbe53e2-0086-47d7-8fc6-0fe048f3dea4
$ ./uuid
c39cbef6-58b3-4945-b845-5c7d2b5a86f7
$ ./uuid
c018d10f-02aa-4a19-99e9-ac2ab86cfbc1
$ ./uuid
417cf67f-cbfa-4032-8642-480d1e9b9e8c
$ ./uuid
c26da70a-fcea-4fdb-8a68-f8b6e4809231

PHP

ボーナスとして、Little Beastが全く同じ方法で実装している為、PHPでもどの様に動作するかを示します。

<?php
  $data = random_bytes(16);
  $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
  $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
  echo vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4))."\n";
?>

$ ls -thal uuid
-rw-r--r--  1 suwako suwako  207B  6月 14 15:21 uuid
$ php ./uuid
53edec61-db8c-4d40-a83c-4acb70a5bc19
$ php ./uuid
35f8d144-4220-474e-bdf2-da162114d774
$ php ./uuid
841d7e8d-1eff-4dc2-afa8-217030d08eeb
$ php ./uuid
2e4caea8-7056-404a-9c3f-9ab3ce2ab8ff
$ php ./uuid
b1c91017-da8a-4988-8ecd-b8f8e7f834b6
$ php ./uuid
3a693006-ce03-43ec-84fe-5b1923fb4dab
$ php ./uuid
cf776682-9968-4fb2-a1c7-10311dc98742
$ php ./uuid
a7eb8a23-8445-4930-88f4-7bb4233ddcd0

結論

以上で、4つの異なる言語で可能な限りシンプルに書かれたUUIDv4ジェネレーターができました。
此れでもう巨大なプログラムは必要ありません、シンプルなジェネレーターだけで十分です。

ソールコード:

以上