UUID v4
128ビットの数値
通常は16進数でxxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxxの形式で表現される
規格はRFC 4122にまとめられてる
バージョン4は乱数によって生成される
データレイアウト
4.1.2項にレイアウトの仕様が書かれている。
table:レイアウト
Field Data Type Octet Note
time_low unsigned32 0-3 The low field of the bit integer timestamp
time_mid unsigned16 4-5 The middle field of the bit integer timestamp
time_hi_and_version unsigned16 6-7 The high field of the bit integer timestamp multiplexed with the version number
clock_seq_hi_and_reserved unsigned8 8 The high field of the bit integer clock sequence multiplexed with the variant
clock_seq_low unsigned8 9 The low field of the clock sequence
node unsigned8 10-15 The spatially unique bit integer node identifier
これをCの構造体にしたのが次
code:c
struct UUID {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_and_version;
uint8_t clock_seq_hi_and_reserved;
uint8_t clock_seq_low;
uint8_t node6;
};
アルゴリズム
4.4項にアルゴリズムが説明されている。
clock_seq_hi_and_reservedの上位2ビットに01をセット
time_hi_and_versionの上位4ビットに4.1.3項に記述されている4ビットのバージョン番号(0100)をセット
その他の全てのビットは乱数をセットする
要するに、128ビット(16バイト)の乱数を生成して、6オクテット目の上位4ビットに0100、8オクテット目の上位2ビットに01をセットすれば良い。
C実装
https://opensource.apple.com/source/xnu/xnu-1486.2.11/libkern/uuid/uuid.c に転がってるソースを読むと、
code:c
void
uuid_generate_random(uuid_t out)
{
read_random(out, sizeof(uuid_t));
out6 = (out6 & 0x0F) | 0x40;
out8 = (out8 & 0x3F) | 0x80;
}
uuid_tの定義は、https://opensource.apple.com/source/xnu/xnu-792.25.20/bsd/uuid/uuid.h にある。
code:c
typedef __darwin_uuid_t uuid_t;
__darwin_uuid_t は https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/_types.h に定義されている
code:c
typedef unsigned char __darwin_uuid_t16;
6オクテット目と8オクテット目のビット操作をしているだけなのがわかる。
JS実装
JavaScriptでランダムなバイト列を生成は、window.crypto.getRandomValues(typedArray) を使うのがはやい
code:js
function uuid4() {
var uu = new Uint8Array(16);
window.crypto.getRandomValues(uu);
uu6 = (uu6 & 0x0F) | 0x40;
uu8 = (uu8 & 0x3F) | 0x80;
return uu;
};
これだとUint8Arrayなので、文字列に変換するのが次の関数
code:js
var ascii = '0123456789abcdef';
var hex = new Array(256);
for (var i = 0; i < 256; i++) {
hexi = asciii >> 4 + asciii & 0x0f;
}
function unparse(uu) {
var i = 0;
return [
hex[uui++], hex[uui++], hex[uui++], hex[uui++], '-',
hex[uui++], hex[uui++], '-',
hex[uui++], hex[uui++], '-',
hex[uui++], hex[uui++], '-',
hex[uui++], hex[uui++], hex[uui++], hex[uui++], hex[uui++], hex[uui++],
].join('');
};