This repository has been archived by the owner on Apr 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathpack.js
157 lines (136 loc) · 4.88 KB
/
pack.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
// Functions to represent a selection of options out of a bigger set
// in a compact, base64 representation.
//
// This is used in the extension to encode which Web API standards should
// be blocked on the current domain, in a way that can be encoded in a cookie
// value.
//
// The two exposed functions in this module are pack and unpack, which are
// inverses of each other. For example:
//
// const options = ["A", "B", "C"];
// const selection = ["B", "C"];
// const base64EncodedSelection = pack(options, selection);
// const decodedSelection = unpack(options, base64EncodedSelection);
// JSON.stringify(decodedSelection) === JSON.stringify(selection) // true
(function () {
"use strict";
const bucketSize = 8;
/**
* Encodes a buffer (such as a Uint8Array) to a base64 encoded string.
*
* @param {ArrayBuffer} buf
* A buffer of binary data.
*
* @return {string}
* A base64 encoded string.
*/
const bufferToBase64 = function (buf) {
const binstr = Array.prototype.map.call(buf, function (ch) {
return String.fromCharCode(ch);
}).join("");
return window.btoa(binstr);
};
const base64StrToBuffer = function (base64str) {
const binstr = window.atob(base64str);
const buf = new Uint8Array(binstr.length);
Array.prototype.forEach.call(binstr, function (ch, i) {
buf[i] = ch.charCodeAt(0);
});
return buf;
};
const binOptionsReduceFunction = function (binSize, prev, next) {
if (prev.length === 0) {
prev.push([next]);
return prev;
}
const mostRecentBin = prev[prev.length - 1];
if (mostRecentBin.length < binSize) {
mostRecentBin.push(next);
return prev;
}
prev.push([next]);
return prev;
};
/**
* Encodes a selection of values, from a larger set of possible options,
* into a base64 encoded bitfield.
*
* The caller should make sure that selected is a subset of options.
*
* The ordering of options and selected do not matter.
*
* This function is the inverse of the `unpack` function from this module.
*
* @param {array} options
* An array of all possible options that might need to be encoded.
* @param {array} selected
* An array containing zero or more elements from option.
*
* @return {string}
* A base64 encoded string, which encodes which elements in `options`
* were in the provided `selected` array.
*/
const pack = function (options, selected) {
const numBuckets = Math.ceil(options.length / bucketSize);
const binToBucketSizeFunc = binOptionsReduceFunction.bind(undefined, bucketSize);
options.sort();
const binnedOptions = options.reduce(binToBucketSizeFunc, []);
const bitFields = new Uint8Array(numBuckets);
let i, j;
for (i = 0; i < numBuckets; i += 1) {
let bitfield = 0;
const currentBucket = binnedOptions[i];
for (j = 0; j < currentBucket.length; j += 1) {
const currentOption = currentBucket[j];
if (selected.indexOf(currentOption) !== -1) {
bitfield |= 1 << j;
}
}
bitFields[i] = bitfield;
}
const encodedString = bufferToBase64(bitFields);
return encodedString;
};
/**
* Decodes a base64 encoded bitfield into an array of values, each of
* which are in the provided options array.
*
* The ordering of the options array does not matter.
*
* This function is the inverse of the `pack` function from this module.
*
* @param {array} options
* An array of all possible options that might need to be encoded.
* @param {string} data
* A base64 encoded string, generated from the `pack` function in
* this module.
*
* @return {array}
* An array of zero or more elements, each of which will be in the
* options array.
*/
const unpack = function (options, data) {
const binToBucketSizeFunc = binOptionsReduceFunction.bind(undefined, bucketSize);
options.sort();
const binnedOptions = options.reduce(binToBucketSizeFunc, []);
const bitFields = base64StrToBuffer(data);
const result = [];
let i, j;
for (i = 0; i < bitFields.length; i += 1) {
const currentBitField = bitFields[i];
const currentOptionsBin = binnedOptions[i];
for (j = 0; j < bucketSize; j += 1) {
if (currentBitField & (1 << j)) {
const currentOption = currentOptionsBin[j];
result.push(currentOption);
}
}
}
return result;
};
window.WEB_API_MANAGER.packingLib = {
pack,
unpack,
};
}());