18548


[ここ](https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64)のjavasciptのbase64
encodeの実装(下記のコード)でbit演算をしていますが、

`var n = (s.charCodeAt(c) << 16) + (s.charCodeAt(c+1) << 8) +
s.charCodeAt(c+2);` (1)

`n = [(n >>> 18) & 63, (n >>> 12) & 63, (n >>> 6) & 63, n & 63];` (2)

なぜ、(1)と(2)で、base64charsのindexに変換できるのでしょうか?  
(1)と(2)にどのような意味があるのでしょうか?

[code]

    
    function base64_encode (s)
    {
      // the result/encoded string, the padding string, and the pad count
      var r = ""; 
      var p = ""; 
      var c = s.length % 3;
    
      // add a right zero pad to make this string a multiple of 3 characters
      if (c > 0) { 
        for (; c < 3; c++) { 
          p += '='; 
          s += "\0"; 
        } 
      }
    
      // increment over the length of the string, three characters at a time
      for (c = 0; c < s.length; c += 3) {
    
        // we add newlines after every 76 output characters, according to the MIME specs
        if (c > 0 && (c / 3 * 4) % 76 == 0) { 
          r += "\r\n"; 
        }
    
        // these three 8-bit (ASCII) characters become one 24-bit number
        var n = (s.charCodeAt(c) << 16) + (s.charCodeAt(c+1) << 8) + s.charCodeAt(c+2); //(1)
    
        // this 24-bit number gets separated into four 6-bit numbers
        n = [(n >>> 18) & 63, (n >>> 12) & 63, (n >>> 6) & 63, n & 63]; // (2)
    
        // those four 6-bit numbers are used as indices into the base64 character list
        r += base64chars[n[0]] + base64chars[n[1]] + base64chars[n[2]] + base64chars[n[3]];
      }
       // add the actual padding string, after removing the zero pad
      return r.substring(0, r.length - p.length) + p;
    }
    
    
    
[/code]


CC BY-SA 3.0
2015-11-08T12:44:24.687
18547
2015-11-08T13:41:35.133
5246
question
javascript base64 encoding
base64のEncodeでのbit演算について


コード中のコメントにも書いてありますが、Base64のエンコードの手続として

  1. 8bit を 1 バイト単位として、3 バイト分をひとかたまりのデータとして扱い、すなわち、24bit のデータを処理単位とする。（24bit に満たない場合は、0 のビットを補って。24bit になるようにする。）
  2. 24bit のデータを、6bit を 1 バイト単位とした、4 バイト分のデータに再分割する。
  3. 各バイトの並びを、変換表に従ってWeb通信上、安全なASCII文字の4文字の並びに変換する。

変換表にあるASCII文字は、HTTP 等でトークン等として使われることのない、安全とされる文字の集合であり（64
よりも少し余りはあると思いますが、ASCII 7bit = 128 文字全部は安全には使えない）、それらのみで表わすと、6bit = 1byte の 64
通りの表現であれば表現可能です。それが、6bit をバイト単位とした 4 バイト分に再分割する理由です。

1) において（2 での再分割前に）、まず 8bit = 1 バイトの 3 文字をひとまとめの 24bit
のデータにして扱う作業を行うわけです。ビット列として考えると、一番左（先頭）の 1 文字は、ビット列 24〜16 ケタ目、真ん中は、15〜8
ケタ目、一番右（末尾）は、7〜0 ケタ目に配置する必要があります。

最後尾は当然、そのままで ok。先頭は 16 ビット分左にビットシフト、真ん中は、8
ビット分左にビットシフトしてから、バイト列として、足し合わせると、そのままビット列が結合された形になります。

[code]

    (s.charCodeAt(c) << 16) + (s.charCodeAt(c+1) << 8) + s.charCodeAt(c+2)
    
[/code]

2) において、1 で得られた 24bit のデータを、6bit = 1 バイトとする単位で、4 バイトに分割して、配列として得ます。

ビット列の分割のために、右シフトと、ビット積を使っています。24bit のビット列中、先頭の 4bit（24〜18 ケタ目）は右に 18
ビット分、ビットシフトすると、5〜0 ケタ目に移動します。同様にして、2 番目（17〜12 ケタ目）は、12 ビット分右シフト、3 番目（11〜6
ケタ目）は 6 ビット分右シフト、4 番目はそのままで、それぞれすべてまずは 5〜0 ケタ目に移動させます。  
そうしてから、5〜0 ケタ目以外の 24〜6 ケタ目のビットは不要なので 111111（= 63）でビット積（ビットマスク）を取り、5〜0
ケタ目をそのまま残してそれ以外のケタを 0 で埋めた状態にしてしまいます。それで目的とする 6 ケタ分だけを取り出せ、6bit = 1 バイト表現による、全
4 バイトの配列となります。

[code]

    [(n >>> 18) & 63, (n >>> 12) & 63, (n >>> 6) & 63, n & 63]
    
[/code]

以下、これを変換表により変換する処理へと続きます。


CC BY-SA 3.0
2015-11-08T13:41:35.133
18548
2015-11-08T13:41:35.133
7290
18547
answer