UUIDバージョン4を生成する

UUIDにはいくつかの変種やバージョンがあるが、その中でも*RFC 4122*の中で定義されているバージョン4(以下v4)は単純に乱数で生成するという非常にシンプルな仕組みである。

そのようなシンプルさのため自前で書くのも容易である。
実際にJavascriptでも書いてみた。

```JavaScript:UUID生成コードの実装例1
class UUID {

  static #uuidIte = ( function* () {

    const HEXOCTETS = Object.freeze( [ ...Array(256) ].map( ( e, i ) => i.toString( 16 ).padStart( 2, "0" ).toUpperCase() ) );
    const VARSION = 0x40;
    const VARIANT = 0x80;

    for (;;) {

      const bytes = crypto.getRandomValues( new Uint8Array( 16 ) );
      yield "" +
        HEXOCTETS[ bytes[ 0 ] ] +
        HEXOCTETS[ bytes[ 1 ] ] +
        HEXOCTETS[ bytes[ 2 ] ] +
        HEXOCTETS[ bytes[ 3 ] ] + "-" +
        HEXOCTETS[ bytes[ 4 ] ] +
        HEXOCTETS[ bytes[ 5 ] ] + "-" +
        HEXOCTETS[ bytes[ 6 ] & 0x0f | VARSION ] +
        HEXOCTETS[ bytes[ 7 ] ] + "-" +
        HEXOCTETS[ bytes[ 8 ] & 0x3f | VARIANT ] +
        HEXOCTETS[ bytes[ 9 ] ] + "-" +
        HEXOCTETS[ bytes[ 10 ] ] +
        HEXOCTETS[ bytes[ 11 ] ] +
        HEXOCTETS[ bytes[ 12 ] ] +
        HEXOCTETS[ bytes[ 13 ] ] +
        HEXOCTETS[ bytes[ 14 ] ] +
        HEXOCTETS[ bytes[ 15 ] ];

    }

  } )();

  static randomUUID() {
  
    return this.#uuidIte.next().value;
  
  }

}

consle.log( UUID.randomUUID() ); //xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```

乱数は`Math.random`でも取得できるが、暗号強度の面で`crypto.getRandomValues`を使用した方が安全である。
また、バイト列であるので表現も簡潔になる。

しかし、意外にも`crypto.getRandomValues`を使用している記事が下記しか見つからなかった。

[JavaScriptの1行小ネタ集
](https://qiita.com/Tksi/items/130bcf82bbfb0379b58b#uuid%E7%94%9F%E6%88%90)

恐らく暗号強度云々よりも「衝突の危険がないIDを簡便に生成したい」というケースがほとんどだと思われるので、`Math.random`を使用したところで危険性が少ないのだろう。

下記に`Math.random`を使用した実装例も記載する。

```Javascript:UUID生成コードの実装例2
class UUID {

  static #uuidIte = ( function* () {

    const HEXOCTETS = Object.freeze( [ ...Array(256) ].map( ( e, i ) => i.toString( 16 ).padStart( 2, "0" ).toUpperCase() ) );
    const VARSION = 0x40;
    const VARIANT = 0x80;

    for (;;) {

      const uint32 = new Uint32Array( 4 ).fill( Math.random() * 0x100000000 >>> 0 );
      yield "" +
        HEXOCTETS[ uint32[ 0 ] & 0xff ] +
        HEXOCTETS[ uint32[ 0 ] >>> 8 & 0xff ] +
        HEXOCTETS[ uint32[ 0 ] >>> 16 & 0xff ] +
        HEXOCTETS[ uint32[ 0 ] >>> 24 & 0xff ] + "-" +
        HEXOCTETS[ uint32[ 1 ] & 0xff ] +
        HEXOCTETS[ uint32[ 1 ] >>> 8 & 0xff ] + "-" +
        HEXOCTETS[ uint32[ 1 ] >>> 16 & 0x0f | VARSION ] +
        HEXOCTETS[ uint32[ 1 ] >>> 24 & 0xff ] + "-" +
        HEXOCTETS[ uint32[ 2 ] & 0x3f | VARIANT ] +
        HEXOCTETS[ uint32[ 2 ] >>> 8 & 0xff ] + "-" +
        HEXOCTETS[ uint32[ 2 ] >>> 16 & 0xff ] +
        HEXOCTETS[ uint32[ 2 ] >>> 24 & 0xff ] +
        HEXOCTETS[ uint32[ 3 ] & 0xff ] +
        HEXOCTETS[ uint32[ 3 ] >>> 8 & 0xff ] +
        HEXOCTETS[ uint32[ 3 ] >>> 16 & 0xff ] +
        HEXOCTETS[ uint32[ 3 ] >>> 24 & 0xff ];

    }

  } )();
  
  static randomUUID() {
  
    return this.#uuidIte.next().value;
  
  }

}
```

実行速度を比較すると`crypto.getRandomValues`より`Math.random`を用いた方が圧倒的に速い。

![Screenshot 2020-12-07 at 11.09.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519665/f21b73c4-949b-d717-7928-a1a2f8b17658.png)

![Screenshot 2020-12-07 at 11.09.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519665/0e1d2427-248a-047b-aa56-d60d3a0319d4.png)

実行環境によっても変わってくるだろうが、仕様を考えるとどの環境も概ね同じ結果になるのではないだろうか。