D言語LispでBase64デコーダを書いた(Oneliner Base64 decoder in Dlang) (通常の実装&解説も一応...)

以前書いたBase64エンコーダ

[D言語で書く "イケメンなコード" ~ Base64エンコーダをワンライナーで書く ~](http://qiita.com/alpha_kai_NET/items/f9fabe15f5619e10e654)

のバグを直した時に、「あっ、まだデコーダを書いてないや」と思ったので書きました。
例によって型推論が効いているので引数の型は省略しました。
今回は記事の末尾にこれのもととなった、ヒューマンリーダブル(?)な通常の実装も載せておきます。

```d:tinyBase64Decoder.d
import std.algorithm,
       std.string,
       std.ascii,
       std.range,
       std.conv;

R delegate(Args) Z(R, Args...)(R delegate(R delegate(Args), Args) f){
  return (Args args) => f(Z(f), args);
}

ubyte[] decode(string data) {
  return
    (convb =>
      (table =>
        (converted =>
          (cLen =>
            (quotients =>
              (qLen =>
                (qLen / 2).iota.map!(i =>
                  (buf =>
                    (bu2 =>
                      ((b0, b1) =>
                        (b0 << 4 ^ b1) & 0xff
                      )(bu2(buf[0]), bu2(buf[1]))
                    )((string bs) => bs.parse!ubyte(2))
                  )(quotients[(i * 2)..((i + 1) * 2)])
                ).array.to!(ubyte[])
              )(quotients.length)
            )(
              (cLen / 4).iota.map!(i =>
                converted[(i * 4)..((i + 1) * 4)]
              ).array
            )
          )(converted.length)
        )(
          data.filter!(x => x != '=').map!(e => 
            table[e.to!string]
          ).array.join
        )
      )((charset => 
          assocArray(
            zip(
              charset,
              charset.length.iota.map!(i =>
                (e =>
                  e.length == 6
                    ? e
                    : repeat("0", 6 - e.length).join ~ e
                )(convb(i, 2)))
              ))
          )((uppercase ~ lowercase ~ digits ~ "+/").split(string.init))
      )
    )((ulong N, int base) =>
      (convbM =>
        convbM(N, N, [], base)
      )(Z((string delegate(ulong, ulong, ulong[], int base) convbM, ulong N, ulong tmp, ulong[] stack, int base) =>
          tmp ? convbM(N, tmp / base, stack ~ (tmp % base), base) : stack.reverse.map!(e => e.to!string).join)));
}

unittest {
  string base    = "ABCDEFG",
         encoded = encode(base);

  assert(base == decode(encoded));
}
```

エンコードに比べて割りと楽でした(ビット演算で少しだけ躓いてしまったので頑張りたい)


では、ヒューマンリーダブルなコードを掲載しておきます(解説も簡単ながら付けておきました):

```d
import std.algorithm,
       std.string,
       std.ascii,
       std.range,
       std.conv;

ubyte[] decodeX(string data) {
  //Base64のデコード用の変換テーブル(Base64文字列 -> 6bit)
  string[string] table =
    ((string delegate(ulong, int) convb) =>
      ((string[] charset) => 
        assocArray(
          zip(
            charset,
            charset.length.iota.map!((ulong i) =>
              ((string e) =>
                e.length == 6
                  ? e
                  : repeat("0", 6 - e.length).join ~ e
              )(convb(i, 2)))
          )
        )
      )((uppercase ~ lowercase ~ digits ~ "+/").split(string.init))
    )((ulong N, int base) =>
      (convbM =>
        convbM(N, N, [], base)
      )(Z((string delegate(ulong, ulong, ulong[], int base) convbM, ulong N, ulong tmp, ulong[] stack, int base) =>
          tmp ? convbM(N, tmp / base, stack ~ (tmp % base), base) : stack.reverse.map!(e => e.to!string).join)));


  //変換表に従い、Base64文字列を6bitに変換し、それを結合
  string converted =
    data.filter!(x => x != '=').map!(e => 
      table[e.to!string]
    ).array.join;

  //4bitずつにconvertedを切り分けるための配列
  string[] quotients;
  //デコードした結果を格納する
  ubyte[] decoded;

  //4文字(4bit)分取り出して、配列に追加する(切り出し)
  foreach (i; (converted.length / 4).iota) {
    string str = converted[(i * 4)..((i + 1) * 4)];
    quotients ~= str;
  }

  //隣接する4bit * 2の8bitからバイナリを復元
  foreach (i; (quotients.length / 2).iota) {
    //隣接する4bit * 2を取り出す
    string[] buf = quotients[(i * 2)..((i + 1) * 2)];

    //文字列化された数字を2進数として解釈し、10進数に変換する
    auto bu2 = ((string bs) => bs.parse!ubyte(2));
    ubyte b0 = bu2(buf[0]),
          b1 = bu2(buf[1]);

    //bit演算してバイナリに戻す
    decoded ~= (b0 << 4 ^ b1) & 0xff;
  }

  /*
    Base64のデコードは数多くの実装が存在するが(検索すると多くの人がすでに実装&解説をしています)、その多くは6bit * 4 = 24bit を 8bit * 3として扱うためにbit演算をしているような気がします。
    しかし、隣接する4bit * 2から計算する方法を用いれば、6bit * 4とするために`quotients` % 4(このコードのquotientsに相当するもの)を別に考える必要がなくなります(たぶん... 自信はないですけど)。
  */

  return decoded;
}
```