-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathbase32.js
251 lines (220 loc) · 6.56 KB
/
base32.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
;(function(){
// This would be the place to edit if you want a different
// Base32 implementation
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
var alias = { o:0, i:1, l:1, s:5 }
/**
* Build a lookup table and memoize it
*
* Return an object that maps a character to its
* byte value.
*/
var lookup = function() {
var table = {}
// Invert 'alphabet'
for (var i = 0; i < alphabet.length; i++) {
table[alphabet[i]] = i
}
// Splice in 'alias'
for (var key in alias) {
if (!alias.hasOwnProperty(key)) continue
table[key] = table['' + alias[key]]
}
lookup = function() { return table }
return table
}
/**
* A streaming encoder
*
* var encoder = new base32.Encoder()
* var output1 = encoder.update(input1)
* var output2 = encoder.update(input2)
* var lastoutput = encode.update(lastinput, true)
*/
function Encoder() {
var skip = 0 // how many bits we will skip from the first byte
var bits = 0 // 5 high bits, carry from one byte to the next
this.output = ''
// Read one byte of input
// Should not really be used except by "update"
this.readByte = function(byte) {
// coerce the byte to an int
if (typeof byte == 'string') byte = byte.charCodeAt(0)
if (skip < 0) { // we have a carry from the previous byte
bits |= (byte >> (-skip))
} else { // no carry
bits = (byte << skip) & 248
}
if (skip > 3) {
// not enough data to produce a character, get us another one
skip -= 8
return 1
}
if (skip < 4) {
// produce a character
this.output += alphabet[bits >> 3]
skip += 5
}
return 0
}
// Flush any remaining bits left in the stream
this.finish = function(check) {
var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '')
this.output = ''
return output
}
}
/**
* Process additional input
*
* input: string of bytes to convert
* flush: boolean, should we flush any trailing bits left
* in the stream
* returns: a string of characters representing 'input' in base32
*/
Encoder.prototype.update = function(input, flush) {
for (var i = 0; i < input.length; ) {
i += this.readByte(input[i])
}
// consume all output
var output = this.output
this.output = ''
if (flush) {
output += this.finish()
}
return output
}
// Functions analogously to Encoder
function Decoder() {
var skip = 0 // how many bits we have from the previous character
var byte = 0 // current byte we're producing
this.output = ''
// Consume a character from the stream, store
// the output in this.output. As before, better
// to use update().
this.readChar = function(char) {
if (typeof char != 'string'){
if (typeof char == 'number') {
char = String.fromCharCode(char)
}
}
char = char.toLowerCase()
var val = lookup()[char]
if (typeof val == 'undefined') {
// character does not exist in our lookup table
return // skip silently. An alternative would be:
// throw Error('Could not find character "' + char + '" in lookup table.')
}
val <<= 3 // move to the high bits
byte |= val >>> skip
skip += 5
if (skip >= 8) {
// we have enough to preduce output
this.output += String.fromCharCode(byte)
skip -= 8
if (skip > 0) byte = (val << (5 - skip)) & 255
else byte = 0
}
}
this.finish = function(check) {
var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '')
this.output = ''
return output
}
}
Decoder.prototype.update = function(input, flush) {
for (var i = 0; i < input.length; i++) {
this.readChar(input[i])
}
var output = this.output
this.output = ''
if (flush) {
output += this.finish()
}
return output
}
/** Convenience functions
*
* These are the ones to use if you just have a string and
* want to convert it without dealing with streams and whatnot.
*/
// String of data goes in, Base32-encoded string comes out.
function encode(input) {
var encoder = new Encoder()
var output = encoder.update(input, true)
return output
}
// Base32-encoded string goes in, decoded data comes out.
function decode(input) {
var decoder = new Decoder()
var output = decoder.update(input, true)
return output
}
/**
* sha1 functions wrap the hash function from Node.js
*
* Several ways to use this:
*
* var hash = base32.sha1('Hello World')
* base32.sha1(process.stdin, function (err, data) {
* if (err) return console.log("Something went wrong: " + err.message)
* console.log("Your SHA1: " + data)
* }
* base32.sha1.file('/my/file/path', console.log)
*/
var crypto, fs
function sha1(input, cb) {
if (typeof crypto == 'undefined') crypto = require('crypto')
var hash = crypto.createHash('sha1')
hash.digest = (function(digest) {
return function() {
return encode(digest.call(this, 'binary'))
}
})(hash.digest)
if (cb) { // streaming
if (typeof input == 'string' || Buffer.isBuffer(input)) {
try {
return cb(null, sha1(input))
} catch (err) {
return cb(err, null)
}
}
if (!typeof input.on == 'function') return cb({ message: "Not a stream!" })
input.on('data', function(chunk) { hash.update(chunk) })
input.on('end', function() { cb(null, hash.digest()) })
return
}
// non-streaming
if (input) {
return hash.update(input).digest()
}
return hash
}
sha1.file = function(filename, cb) {
if (filename == '-') {
process.stdin.resume()
return sha1(process.stdin, cb)
}
if (typeof fs == 'undefined') fs = require('fs')
return fs.stat(filename, function(err, stats) {
if (err) return cb(err, null)
if (stats.isDirectory()) return cb({ dir: true, message: "Is a directory" })
return sha1(require('fs').createReadStream(filename), cb)
})
}
var base32 = {
Decoder: Decoder,
Encoder: Encoder,
encode: encode,
decode: decode,
sha1: sha1
}
if (typeof window !== 'undefined') {
// we're in a browser - OMG!
window.base32 = base32
}
if (typeof module !== 'undefined' && module.exports) {
// nodejs/browserify
module.exports = base32
}
})();