torutkのブログ

ソフトウェア・エンジニアのブログ

整数データとバイト配列の変換

RFC 4193「一意なローカルIPv6アドレス」では、IPv4のプライベートアドレスのような使い方をするアドレスについて規定しています。家で数台のPCでIPv6の実験(勉強)するときは、この一意なローカルIPv6アドレスを使用するのがよいです。

http://www5d.biglobe.ne.jp/~stssk/rfc/rfc4193j.html

で、RFC 4193では一意なIDを生成する方法が記載されています。NTPタイムスタンプとEUI-64を合成し、SHA-1でハッシュを生成し、その40bitを取るアルゴリズムです。

NTPタイムスタンプは64bit固定小数点なので、ハッシュ計算するためにbyte配列に変換したいところです。力技で8bitずつシフトして下位1バイトをマスクして取り出し配列に詰める方法はありますが、面倒です。とりあえず思いつく方法を挙げてみると

  1. java.util.BigIntegerのtoByteArray()を使う
  2. java.nio.ByteBufferのget(byte[])を使う
  3. java.io.ByteArrayOutputStreamにDataOutputStreamをかぶせてwriteLong()を使う

これから実験にとりかかります。

java.nio.ByteBuffer

今回の処理には、一番ByteBufferが向いているようです。
NTPタイムスタンプの8バイトとEUI-64の8バイトを結合して16バイトのデータを作るので、まずByteBufferを16バイトサイズで作成します。

    ByteBuffer buffer = ByteBuffer.allocate(16);

allocateメソッドは、通常のヒープ(ガベージコレクション対象)にバッファ領域を作ります。一方allocateDirectメソッドはバッファを介さずチャネル(I/O)を直接読み書きします。

バッファにlong値を書き込みます。

  buffer.putLong(ntpTime);

デフォルトではビッグエンディアンで書き込みます。longなので書き込み後バッファ上のpositionは8進みます。

バッファにバイト配列(8バイト)を書き込みます。

  buffer.put(eui64);

バイト配列をそのままの順序で書き込み、配列のlength分だけ(今回は8)positionを進めます。

ここまでで16バイトのデータが書かれました。次はこの16バイトを配列として取り出します。

  byte[] data = new byte[16];
  buffer.rewind();
  buffer.get(data);

まず16バイトのデータ領域を確保し、rewindメソッドでpositionを0に戻します。それからgetメソッドでバイトバッファに書き込まれたデータを取り出します。