-
Notifications
You must be signed in to change notification settings - Fork 3
/
charSet.js
89 lines (75 loc) · 2.4 KB
/
charSet.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
const BITS_PER_BYTE = 8
const {
abs, ceil, floor, log2
} = Math
const gcd = (a, b) => {
let la = a
let lb = b
while (lb !== 0) {
[la, lb] = [lb, la % lb]
}
return abs(la)
}
const lcm = (a, b) => (a / gcd(a, b)) * b
const genNdxFn = (bitsPerChar) => {
// If BITS_PER_BYTEs is a multiple of bitsPerChar, we can slice off an integer number
// of chars per byte.
if (lcm(bitsPerChar, BITS_PER_BYTE) === BITS_PER_BYTE) {
return (chunk, slice, bytes) => {
const lShift = bitsPerChar
const rShift = BITS_PER_BYTE - bitsPerChar
return ((bytes[chunk] << (lShift * slice)) & 0xff) >> rShift
}
}
// Otherwise, while slicing off bits per char, we can possibly straddle two
// of bytes, so a more work is involved
const slicesPerChunk = lcm(bitsPerChar, BITS_PER_BYTE) / BITS_PER_BYTE
return (chunk, slice, bytes) => {
const bNum = chunk * slicesPerChunk
const offset = (slice * bitsPerChar) / BITS_PER_BYTE
const lOffset = floor(offset)
const rOffset = ceil(offset)
const rShift = BITS_PER_BYTE - bitsPerChar
const lShift = (slice * bitsPerChar) % BITS_PER_BYTE
let ndx = ((bytes[bNum + lOffset] << lShift) & 0xff) >> rShift
const r1Bits = (rOffset + 1) * BITS_PER_BYTE
const s1Bits = (slice + 1) * bitsPerChar
const rShiftIt = (r1Bits - s1Bits) % BITS_PER_BYTE
if (rShift < rShiftIt) {
ndx += bytes[bNum + rOffset] >> rShiftIt
}
return ndx
}
}
class CharSet {
constructor(chars) {
if (!(typeof chars === 'string' || chars instanceof String)) {
throw new Error('Invalid chars: Must be string')
}
const { length } = chars
if (![2, 4, 8, 16, 32, 64].includes(length)) {
throw new Error('Invalid char count: must be one of 2,4,8,16,32,64')
}
// Ensure no repeated characters
for (let i = 0; i < length; i += 1) {
const c = chars.charAt(i)
for (let j = i + 1; j < length; j += 1) {
if (c === chars.charAt(j)) {
throw new Error('Characters not unique')
}
}
}
this.chars = chars
this.bitsPerChar = floor(log2(length))
this.length = length
this.ndxFn = genNdxFn(this.bitsPerChar)
this.charsPerChunk = lcm(this.bitsPerChar, BITS_PER_BYTE) / this.bitsPerChar
}
bytesNeeded(bitLen) {
const count = ceil(bitLen / this.bitsPerChar)
return ceil((count * this.bitsPerChar) / BITS_PER_BYTE)
}
}
module.exports = {
CharSet
}