菊池 Blog

移転しました 続・菊池 和彦の足跡

AILight Banner
AILight Blog

プロフィール

菊池 Blog

目次

Blog 利用状況

記事分類

過去の記事

タグ

OpenID2.0 を直接blog上で実装してみる(その2)

OpenID2.0 を直接blog上で実装してみる(その1) の続き

今日は 4.2.  Integer Representations を実装してみる。

単純にバイトを \x00 形式で出すには "\x"+byte.ToString("X2") でいい、これを上位→下位のBigEndianで出す、頭が\x00でなければ\x00を補う必要がある…細かいなぁ。

そんなことを考えるぐらいなら int.ToString("X") をまずしてしまい、文字数が偶数になるように先頭に0を付与、先頭が00でなければ00を付与して2文字毎に取り出して\xを補っても一緒、頭で連続する 00 を取っ払ってから 00 を付与し、2文字ごとに\xをつけて出す。

public string BTWOC( int x )
{
    string hex = int.ToString("X8");
    if( hex.Length%2!=0 ) hex = "0"+hex;
    if( !hex.StartsWith("00") ) hex = "00"+hex;
    StringBuilder result = new StringBuilder()
    for( int i=0;i<hex.Length;i+=2 )
    {
        result.Append("\\x");
        result.Append( hex.SubString(i,2) );
    }
    return result;
}

無駄な要素を探すと hex="00"+hex をしているところは result.Append("\\x00")としても一緒だ。これで中間で生成される文字列(しかもループ内で!!)を除外できる。

public string BTWOC( int x )
{
    string hex = int.ToString("X8");
    if( hex.Length%2!=0 ) hex = "0"+hex;
    StringBuilder result = new StringBuilder()
    if( !hex.StartsWith("00") ) result.Append("\\x00");
    for( int i=0;i<hex.Length;i+=2 )
    {
        result.Append("\\x");
        result.Append( hex.SubString(i,2) );
    }
    return result;
}

文字列の切り出し作業をしながら StringBuilder に Append して行くとついつい書いてしまう上記のコード、StringBuilder.Append には SubStringを内在するオーバーロードがある。これでStringのインスタンスができてしまう事を防げる。

public string BTWOC( int x )
{
    string hex = int.ToString("X");
    if( hex.Length%2!=0 ) hex = "0"+hex;
    StringBuilder result = new StringBuilder()
    if( !hex.StartsWith("00") ) result.Append("\\x00");
    for( int i=0;i<hex.Length;i+=2 )
    {
        result.Append("\\x");
        result.Append( hex,i,2 );
    }
    return result;
}

とことんケチるつもりなら hex="0"+hex で生成される文字列も消す事ができるが、コードの記述量に比べてメリットが薄いからこんなもんで良いだろう。

では、この文字列を int に戻すコードを考えよう。文字列の先頭から\x を読み込んだら次の2文字をbyteとして取得していく。\xが見つからなくなるまで繰り返して終了だ。

public int FromBTWOC( string val )
{
     int result =0;
     for( int i=0;i<val.Length;i+=4 ) {
        if( val[i]=='\\' && val[i+1]=='x' ) {
            byte b = byte.Parse( val.SubString(i,2),NumberStyles.AllowHexSpecifier );
            result = result <<8 + b;
        } else break;
    }
    return result;
}

byte.ParseはSubStringを内在するオーバーロードを持たない。 val.SubString の結果が byte.Parse に渡される中間のstring が嫌いな人は結構苦労することになる。しかしあえて無視する。この関数はそんなに使われないからだ。

 

さて、Integer Representations という事で int としてみてきたが実際はどうなのか。

8.1.2.  Diffie-Hellman Request Parameters を見ると

base64(btwoc(p)) という使われ方をしている、btwocのパラメータに与えられているパラメータは DH-SHAでハッシュされるキーだ、勿論 int の32bitの様な短いキーなどありえない。結局は byte[] で扱うことになる。

そして base64エンコードされるので出力はstringよりもbyte配列のほうが良い。(ASCII と UNICODEの間で無駄な変換をする必要もない)

ということは byte[] BTOWC( byte[] ) こそが実装すべき本命という事になる。

class BTOWCEncoder
{
const byte slash = (byte)'\\';
const byte hexMark = (byte)'x';
static readonly byte[] hexChars;

static BTOWCEncoder()
{
    hexChars = new byte[] { (byte)'0',    (byte)'1', .... (byte)'9', (byte)'A', ... (byte)'F' };
}

static byte[] BTOWC( byte[] input )
{
    int outPos =0;
    byte[] result;
    if( input[0] ==0 ) {
        result = new byte[ (input.Length+1)*4 ]; 
        outPos = PutResult( result,outPos,0 );
    } else {
        resule = new byte[ input.Length*4];
    }
    for( int i=0;i<input.Length;i++ ) {
         outPus = PutResult( result,outPus,input[i] );
    }
    return result;
}
static int PutResult( byte[] putTo,int start, byte data )
{
     putTo[start] = slash; start++;
     putTo[start] = hexMark; start++;
     putTo[start] = hexChars[data/16]; start++;
     putTo[start] = hexChars[data%16]; start++;
     return start;
}
}

というわけで、これがあればきっと幸せだ。

コードではbyte値しか使っていないしbyte配列が作られるのは明示的なnewのみなのでどこかで中間オブジェクトが無駄に作られることもない、このコードのパフォーマンスについてはunsafeコンテキストでポインタとか使わない限りには(使ったとしても微々たる改善しか)望めないだろう。

続きはまた明日以降でいってみよう。

#blog上で直接コードしてるのでコンパイルエラーとかあるかもねー

投稿日時 : 2007年12月11日 14:58


コメントを追加

タイトル
名前
URL
コメント