Passed
Pull Request — master (#1)
by thomas
01:18
created

index.js ➔ fromWords   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
'use strict';
2
3
var BigInteger = require('bigi');
4
var ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
5
6
// pre-compute lookup table
7
var SEPARATOR = ':';
8
var CSLEN = 8;
9
var ALPHABET_MAP = {};
10
for (var z = 0; z < ALPHABET.length; z++) {
11
    var x = ALPHABET.charAt(z);
12
    if (ALPHABET_MAP[x] !== undefined) {
13
        throw new TypeError(x + ' is ambiguous');
14
    }
15
    ALPHABET_MAP[x] = z;
16
}
17
18
function polymodStep(pre) {
19
    var b = pre.shiftRight(35);
20
    var mask = BigInteger.fromHex('07ffffffff');
21
22
    var v = pre.and(mask).shiftLeft(new BigInteger('5'));
23
24
    if (b.and(new BigInteger('1')).intValue() > 0) {
25
        v = v.xor(BigInteger.fromHex('98f2bc8e61'));
26
    }
27
    if (b.and(new BigInteger('2')).intValue()) {
28
        v = v.xor(BigInteger.fromHex('79b76d99e2'));
29
    }
30
    if (b.and(new BigInteger('4')).intValue()) {
31
        v = v.xor(BigInteger.fromHex('f33e5fb3c4'));
32
    }
33
    if (b.and(new BigInteger('8')).intValue()) {
34
        v = v.xor(BigInteger.fromHex('ae2eabe2a8'));
35
    }
36
    if (b.and(new BigInteger('16')).intValue()) {
37
        v = v.xor(BigInteger.fromHex('1e4f43e470'));
38
    }
39
40
    return v;
41
}
42
43
function prefixChk(prefix) {
44
    var chk = new BigInteger('1');
45
    for (var i = 0; i < prefix.length; ++i) {
46
        var c = prefix.charCodeAt(i);
47
48
        var mixwith = new BigInteger('' + (c & 0x1f));
49
        chk = polymodStep(chk).xor(mixwith);
50
    }
51
52
    chk = polymodStep(chk);
53
    return chk;
54
}
55
56
function encode(prefix, words) {
57
    // too long?
58
    if (prefix.length + CSLEN + 1 + words.length > 90) {
59
        throw new TypeError('Exceeds Base32 maximum length');
60
    }
61
62
    prefix = prefix.toLowerCase();
63
64
    // determine chk mod
65
    var chk = prefixChk(prefix);
66
    var result = prefix + SEPARATOR;
67
    for (var i = 0; i < words.length; ++i) {
68
        var _x = words[i];
69
        if (_x >>> 5 !== 0) {
70
            throw new Error('Non 5-bit word');
71
        }
72
73
        chk = polymodStep(chk).xor(new BigInteger('' + _x));
74
        result += ALPHABET.charAt(_x);
75
    }
76
77
    for (var _i = 0; _i < CSLEN; ++_i) {
78
        chk = polymodStep(chk);
79
    }
80
    chk = chk.xor(new BigInteger('1'));
81
    for (var _i2 = 0; _i2 < CSLEN; ++_i2) {
82
        var pos = 5 * (CSLEN - 1 - _i2);
83
        var v2 = chk.shiftRight(new BigInteger('' + pos)).and(BigInteger.fromHex('1f'));
84
        result += ALPHABET.charAt(v2.toString(10));
85
    }
86
87
    return result;
88
}
89
90
function decode(str) {
91
    if (str.length < 8) throw new TypeError(str + ' too short');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
92
    if (str.length > 90) throw new TypeError(str + ' too long');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
93
94
    // don't allow mixed case
95
    var lowered = str.toLowerCase();
96
    var uppered = str.toUpperCase();
97
    if (str !== lowered && str !== uppered) {
98
        throw new Error('Mixed-case string ' + str);
99
    }
100
101
    str = lowered;
102
103
    var split = str.lastIndexOf(SEPARATOR);
104
    if (split === -1) {
105
        throw new Error('No separator character for ' + str);
106
    }
107
108
    if (split === 0) {
109
        throw new Error('Missing prefix for ' + str);
110
    }
111
112
    var prefix = str.slice(0, split);
113
    var wordChars = str.slice(split + 1);
114
    if (wordChars.length < 6) {
115
        throw new Error('Data too short');
116
    }
117
118
    var chk = prefixChk(prefix);
119
    var words = [];
120
    for (var i = 0; i < wordChars.length; ++i) {
121
        var c = wordChars.charAt(i);
122
        var v = ALPHABET_MAP[c];
123
        if (v === undefined) {
124
            throw new Error('Unknown character ' + c);
125
        }
126
127
        chk = polymodStep(chk).xor(new BigInteger('' + v));
128
        // not in the checksum?
129
        if (i + CSLEN >= wordChars.length) continue;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
130
        words.push(v);
131
    }
132
133
    if (chk.toString(10) !== '1') {
134
        throw new Error('Invalid checksum for ' + str);
135
    }
136
137
    return { prefix: prefix, words: words };
138
}
139
140
function convert(data, inBits, outBits, pad) {
141
    var value = 0;
142
    var bits = 0;
143
    var maxV = (1 << outBits) - 1;
144
145
    var result = [];
146
    for (var i = 0; i < data.length; ++i) {
147
        value = value << inBits | data[i];
148
        bits += inBits;
149
150
        while (bits >= outBits) {
151
            bits -= outBits;
152
            result.push(value >>> bits & maxV);
153
        }
154
    }
155
156
    if (pad) {
157
        if (bits > 0) {
158
            result.push(value << outBits - bits & maxV);
159
        }
160
    } else {
161
        if (bits >= inBits) {
162
            throw new Error('Excess padding');
163
        }
164
        if (value << outBits - bits & maxV) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
165
            throw new Error('Non-zero padding');
166
        }
167
    }
168
169
    return result;
170
}
171
172
function toWords(bytes) {
173
    return convert(bytes, 8, 5, true);
174
}
175
176
function fromWords(words) {
177
    return convert(words, 5, 8, false);
178
}
179
180
module.exports = { decode: decode, encode: encode, toWords: toWords, fromWords: fromWords };