Passed
Push — master ( 7d8ce1...3a91ad )
by thomas
01:07
created

src/base32.js   B

Complexity

Total Complexity 37
Complexity/F 5.29

Size

Lines of Code 185
Function Count 7

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 99.03%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
nc 2
dl 0
loc 185
ccs 102
cts 103
cp 0.9903
crap 2
rs 8.6
wmc 37
mnd 2
bc 37
fnc 7
bpm 5.2857
cpm 5.2857
noi 1

7 Functions

Rating   Name   Duplication   Size   Complexity  
A base32.js ➔ toWords 0 3 1
A base32.js ➔ prefixChk 0 12 2
B base32.js ➔ encode 0 33 6
A base32.js ➔ fromWords 0 3 1
C base32.js ➔ convert 0 31 7
B base32.js ➔ polymodStep 0 24 6
C base32.js ➔ decode 0 55 12
1
'use strict'
2 1
let BigInteger = require('bigi')
3 1
let ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
4
5
// pre-compute lookup table
6 1
let SEPARATOR = ':'
7 1
let CSLEN = 8
8 1
let ALPHABET_MAP = {}
9 1
for (let z = 0; z < ALPHABET.length; z++) {
10 32
    let x = ALPHABET.charAt(z)
11 32
    if (ALPHABET_MAP[x] !== undefined) {
12
        throw new TypeError(x + ' is ambiguous')
13
    }
14 32
    ALPHABET_MAP[x] = z
15
}
16
17
function polymodStep (pre) {
18 2432
    let b = pre.shiftRight(35)
19 2432
    let mask = BigInteger.fromHex('07ffffffff')
20
21 2432
    let v = pre.and(mask).shiftLeft(new BigInteger('5'))
22
23 2432
    if (b.and(new BigInteger('1')).intValue() > 0) {
24 1080
        v = v.xor(BigInteger.fromHex('98f2bc8e61'))
25
    }
26 2432
    if (b.and(new BigInteger('2')).intValue()) {
27 1033
        v = v.xor(BigInteger.fromHex('79b76d99e2'))
28
    }
29 2432
    if (b.and(new BigInteger('4')).intValue()) {
30 1030
        v = v.xor(BigInteger.fromHex('f33e5fb3c4'))
31
    }
32 2432
    if (b.and(new BigInteger('8')).intValue()) {
33 1141
        v = v.xor(BigInteger.fromHex('ae2eabe2a8'))
34
    }
35 2432
    if (b.and(new BigInteger('16')).intValue()) {
36 1123
        v = v.xor(BigInteger.fromHex('1e4f43e470'))
37
    }
38
39 2432
    return v
40
}
41
42
function prefixChk (prefix) {
43 48
    let chk = new BigInteger('1')
44 48
    for (let i = 0; i < prefix.length; ++i) {
45 483
        let c = prefix.charCodeAt(i)
46
47 483
        let mixwith = new BigInteger('' + (c & 0x1f))
48 483
        chk = polymodStep(chk).xor(mixwith)
49
    }
50
51 48
    chk = polymodStep(chk)
52 48
    return chk
53
}
54
55
function encode (prefix, words) {
56
    // too long?
57 18
    if ((prefix.length + CSLEN + 1 + words.length) > 90) {
58 1
        throw new TypeError('Exceeds Base32 maximum length')
59
    }
60
61 17
    prefix = prefix.toLowerCase()
62
63
    // determine chk mod
64 17
    let chk = prefixChk(prefix)
65 17
    let result = prefix + SEPARATOR
66 17
    for (let i = 0; i < words.length; ++i) {
67 547
        let x = words[i]
68 547
        if ((x >>> 5) !== 0) {
69 1
            throw new Error('Non 5-bit word')
70
        }
71
72 546
        chk = polymodStep(chk).xor(new BigInteger('' + x))
73 546
        result += ALPHABET.charAt(x)
74
    }
75
76 16
    for (let i = 0; i < CSLEN; ++i) {
77 128
        chk = polymodStep(chk)
78
    }
79 16
    chk = chk.xor(new BigInteger('1'))
80 16
    for (let i = 0; i < CSLEN; ++i) {
81 128
        let pos = 5 * (CSLEN - 1 - i)
82 128
        let v2 = chk.shiftRight(new BigInteger('' + pos)).and(BigInteger.fromHex('1f'))
83 128
        result += ALPHABET.charAt(v2.toString(10))
84
    }
85
86 16
    return result
87
}
88
89
function decode (str) {
90 37
    if (str.length < 8) {
91 1
        throw new TypeError(str + ' too short')
92
    }
93 36
    if (str.length > 90) {
94 1
        throw new TypeError(str + ' too long')
95
    }
96
97
    // don't allow mixed case
98 35
    let lowered = str.toLowerCase()
99 35
    let uppered = str.toUpperCase()
100 35
    if (str !== lowered && str !== uppered) {
101 1
        throw new Error('Mixed-case string ' + str)
102
    }
103
104 34
    str = lowered
105
106 34
    let split = str.lastIndexOf(SEPARATOR)
107 34
    if (split === -1) {
108 1
        throw new Error('No separator character for ' + str)
109
    }
110
111 33
    if (split === 0) {
112 1
        throw new Error('Missing prefix for ' + str)
113
    }
114
115 32
    let prefix = str.slice(0, split)
116 32
    let wordChars = str.slice(split + 1)
117 32
    if (wordChars.length < 6) {
118 1
        throw new Error('Data too short')
119
    }
120
121 31
    let chk = prefixChk(prefix)
122 31
    let words = []
123 31
    for (let i = 0; i < wordChars.length; ++i) {
124 1228
        let c = wordChars.charAt(i)
125 1228
        let v = ALPHABET_MAP[c]
126 1228
        if (v === undefined) {
127 1
            throw new Error('Unknown character ' + c)
128
        }
129
130 1227
        chk = polymodStep(chk).xor(new BigInteger('' + v))
131
        // not in the checksum?
132 1227
        if (i + CSLEN >= wordChars.length) {
133 240
            continue
134
        }
135 987
        words.push(v)
136
    }
137
138 30
    if (chk.toString(10) !== '1') {
139 9
        throw new Error('Invalid checksum for ' + str)
140
    }
141
142 21
    return { prefix, words }
143
}
144
145
function convert (data, inBits, outBits, pad) {
146 39
    let value = 0
147 39
    let bits = 0
148 39
    let maxV = (1 << outBits) - 1
149
150 39
    let result = []
151 39
    for (let i = 0; i < data.length; ++i) {
152 1058
        value = (value << inBits) | data[i]
153 1058
        bits += inBits
154
155 1058
        while (bits >= outBits) {
156 973
            bits -= outBits
157 973
            result.push((value >>> bits) & maxV)
158
        }
159
    }
160
161 39
    if (pad) {
162 16
        if (bits > 0) {
163 16
            result.push((value << (outBits - bits)) & maxV)
164
        }
165
    } else {
166 23
        if (bits >= inBits) {
167 1
            throw new Error('Excess padding')
168
        }
169 22
        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...
170 1
            throw new Error('Non-zero padding')
171
        }
172
    }
173
174 37
    return result
175
}
176
177
function toWords (bytes) {
178 16
    return convert(bytes, 8, 5, true)
179
}
180
181
function fromWords (words) {
182 23
    return convert(words, 5, 8, false)
183
}
184
185
module.exports = { decode, encode, toWords, fromWords }
186