forked from EntropyString/JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcharSet.js
More file actions
112 lines (95 loc) · 3.12 KB
/
charSet.js
File metadata and controls
112 lines (95 loc) · 3.12 KB
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
import lcm from './lcm'
import WeakMap from 'weak-map'
const propMap = new WeakMap()
const BITS_PER_BYTE = 8
export default class CharSet {
constructor(chars) {
if (!(typeof chars === 'string' || chars instanceof String)) {
throw new Error('Invalid chars: Must be string')
}
const length = chars.length
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')
}
const bitsPerChar = Math.floor(Math.log2(length))
// Ensure no repeated characters
for (let i = 0; i < length; i++) {
let c = chars.charAt(i)
for (let j = i+1; j < length; j++) {
if (c === chars.charAt(j)) {
throw new Error('Characters not unique')
}
}
}
const privProps = {
chars,
bitsPerChar,
length,
ndxFn: _ndxFn(bitsPerChar),
charsPerChunk: lcm(bitsPerChar, BITS_PER_BYTE) / bitsPerChar
}
propMap.set(this, privProps)
}
getChars() {
return propMap.get(this).chars
}
getBitsPerChar() {
return propMap.get(this).bitsPerChar
}
getNdxFn() {
return propMap.get(this).ndxFn
}
getCharsPerChunk() {
return propMap.get(this).charsPerChunk
}
length() {
return propMap.get(this).length
}
getNdxFn() {
return propMap.get(this).ndxFn
}
bytesNeeded(entropyBits) {
const count = Math.ceil(entropyBits / this.bitsPerChar())
return Math.ceil(count * this.bitsPerChar() / BITS_PER_BYTE)
}
// Aliases
chars() { return this.getChars() }
ndxFn() { return this.getNdxFn() }
bitsPerChar() { return this.getBitsPerChar() }
}
const _ndxFn = (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 function(chunk, slice, bytes) {
let lShift = bitsPerChar
let rShift = BITS_PER_BYTE - bitsPerChar
return ((bytes[chunk]<<(lShift*slice))&0xff)>>rShift
}
}
// Otherwise, while slicing off bits per char, we will possibly straddle a couple
// of bytes, so a bit more work is involved
else {
let slicesPerChunk = lcm(bitsPerChar, BITS_PER_BYTE) / BITS_PER_BYTE
return function(chunk, slice, bytes) {
let bNum = chunk * slicesPerChunk
let offset = (slice*bitsPerChar)/BITS_PER_BYTE
let lOffset = Math.floor(offset)
let rOffset = Math.ceil(offset)
let rShift = BITS_PER_BYTE - bitsPerChar
let lShift = (slice*bitsPerChar) % BITS_PER_BYTE
let ndx = ((bytes[bNum+lOffset]<<lShift)&0xff)>>rShift
let rShiftIt = ((rOffset+1)*BITS_PER_BYTE - (slice+1)*bitsPerChar) % BITS_PER_BYTE
if (rShift < rShiftIt) {
ndx += bytes[bNum+rOffset]>>rShiftIt
}
return ndx
}
}
}
export let charSet64 = new CharSet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_')
export let charSet32 = new CharSet('2346789bdfghjmnpqrtBDFGHJLMNPQRT')
export let charSet16 = new CharSet('0123456789abcdef')
export let charSet8 = new CharSet('01234567')
export let charSet4 = new CharSet('ATCG')
export let charSet2 = new CharSet('01')