45464


JavaScriptでascii85encoderを作っているのですが、encode後の値がすこし違うものになってしまいます。  
なぜなのでしょうか?

例えば、ascii85 Python
encoder(base64.a85encode)に"Hello"のバイト列を渡すと`87cURDZ`となりますが、この関数(ascii85encode)では`87cURDZBb;`となってしまいます。

[code]

    function ascii85encode(arr) {
        let str = "";
        let a = [];
        for (let i = 0; i < arr.length; i++) {
            a.push(arr[i]);
            if (a.length === 4) {
                let res = ascii85_str(a);
                str += res;
                a = [];
            }
        }
        if (a.length > 0) {
            for (let t = 0; t < 4 - a.length; t++) {
                //足りないところを0で埋める。
                a.push(0);
            }
            str += ascii85_str(a, true);
        }
        return str
    };
    
    function ascii85_str(nums, last=false) {
        const uint32 = createUInt32(nums);
        const str = base85_num(uint32, last);
        if (str === "!!!!!") {
            str = "z";
        }
        return str;
    };
    
    function createUInt32(a) {
        if (a.length > 4) {
            throw new Error("createUInt32 argument must be a four elements list of UInt8");
        }
        return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]);
    };
    
    function base85_num(n, l = false) {
        //引数としてわたってきたブロックの値(32bit値)を85進数の値(0~84)に変換
        n = Math.abs(n);
        let res = [];
        for (let p = 0; p < 5; p++) {
            let s = String.fromCodePoint((n%85) + 33)
            res.unshift(s);
            n = parseInt(n / 85);
            if (l && n === 0) { //与えられた値が最後のブロックでかつ、nがゼロ(これ以上余りが出ない)ならそこで終了
                break;
            }
        }
        return res.join("");
    };
    
    
    export { ascii85encode };
    
[/code]


CC BY-SA 4.0
2018-07-09T15:25:33.293
45462
2018-07-09T18:03:03.217
2018-07-09T15:30:48.250
5246
5246
question
javascript
JavaScriptで書いたascii85 Encoderがうまく動かない


最終ブロックの処理が間違っています。

[code]

        if (l && n === 0) { //与えられた値が最後のブロックでかつ、nがゼロ(これ以上余りが出ない)ならそこで終了
    
[/code]

`n`は余りではなく商であり、あなたのコードは下位桁から処理していっているので、上位桁の情報を持っています。たとえ`n`がたまたま`0`になっても途中で終了してはいけません。

バイト数が4の倍数にならずに(256進数の意味で)`0`埋めした分は、解読時に影響しない範囲で省略します。

(256進で)`0`3個 => 85^3 < 256^3 < 85^4

と言うわけで、85進数と言える結果の文字数を4つ無視すると、下位3バイト分以上の差になってしまうので、`0`埋めを3バイト分行なった時に無視して良い85進側の文字数は3と言うことになります。

理屈をこねると難しくなりますが、`0`埋め2個なら85進側でも2文字、1個なら1文字という結果が出ます。

つまり`0`埋めした個数の分だけ結果の下位文字を削らないといけません。

以下の2関数を変更する必要があるでしょう。

[code]

    function ascii85encode(arr) {
        let str = "";
        let a = [];
        for (let i = 0; i < arr.length; i++) {
            a.push(arr[i]);
            if (a.length === 4) {
                let res = ascii85_str(a);
                str += res;
                a = [];
            }
        }
        if (a.length > 0) {
            let padding = 4 - a.length;
            for (let t = 0; t < padding; t++) {
                //足りないところを0で埋める。
                a.push(0);
            }
            str += ascii85_str(a, padding); //<-最終ブロックのpaddingバイト数を覚えておく
        }
        return str
    };
    
    function base85_num(n, padding = 0) {
        //引数としてわたってきたブロックの値(32bit値)を85進数の値(0~84)に変換
        n = Math.abs(n);
        let res = [];
        for (let p = 0; p < 5; p++) {
            let s = String.fromCodePoint((n%85) + 33)
            res.unshift(s);
            n = parseInt(n / 85);
        }
        res = res.slice(0, 5-padding); //<- paddingバイト数の分後ろを削る
        return res.join("");
    };
    
[/code]


CC BY-SA 4.0
2018-07-09T18:03:03.217
45464
2018-07-09T18:03:03.217
13972
45462
answer