id_pack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +21 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/lib/id-packer.js +296 -0
- data/app/assets/javascripts/lib/lz-string.js +511 -0
- data/app/assets/javascripts/lib/uuid-packer.js +372 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/id_pack.gemspec +37 -0
- data/lib/id_pack/engine.rb +3 -0
- data/lib/id_pack/id_packer.rb +395 -0
- data/lib/id_pack/lz_string.rb +579 -0
- data/lib/id_pack/uuid_packer.rb +352 -0
- data/lib/id_pack/version.rb +3 -0
- data/lib/id_pack.rb +6 -0
- data/vendor/assets/javascripts/require.js +2145 -0
- metadata +108 -0
@@ -0,0 +1,372 @@
|
|
1
|
+
/** vim: et:ts=2:sw=2:sts=2
|
2
|
+
* @license (c) 2017 Ribose Inc.
|
3
|
+
*/
|
4
|
+
(function (factory) {
|
5
|
+
'use strict';
|
6
|
+
|
7
|
+
/* AMD. Register as an anonymous module. */
|
8
|
+
if (typeof define === 'function' && define.amd) {
|
9
|
+
define(factory);
|
10
|
+
}
|
11
|
+
|
12
|
+
/* Node/CommonJS */
|
13
|
+
else if (typeof exports === 'object') {
|
14
|
+
module.exports = factory();
|
15
|
+
}
|
16
|
+
|
17
|
+
/* Browser globals */
|
18
|
+
else {
|
19
|
+
factory();
|
20
|
+
}
|
21
|
+
|
22
|
+
}(function() {
|
23
|
+
'use strict';
|
24
|
+
|
25
|
+
var UNIT_LEN = 32; // a constant = length of each of the archived strings, for UUID is 32, in other cases it can vary
|
26
|
+
|
27
|
+
return {
|
28
|
+
|
29
|
+
// calculate bits in number
|
30
|
+
binPow: function(num) {
|
31
|
+
var pow = 0;
|
32
|
+
while (num >> pow !== 0) { pow += 1; }
|
33
|
+
return pow;
|
34
|
+
},
|
35
|
+
|
36
|
+
// calculate bits in the string that is hexadecimal number
|
37
|
+
strPow: function (str) {
|
38
|
+
return binPow(parseInt(str, 16));
|
39
|
+
},
|
40
|
+
|
41
|
+
// instead Ruby string reverse
|
42
|
+
revString: function (str) {
|
43
|
+
var
|
44
|
+
splitString = str.split(""),
|
45
|
+
reverseArray = splitString.reverse(),
|
46
|
+
joinArray = reverseArray.join("");
|
47
|
+
|
48
|
+
return joinArray;
|
49
|
+
},
|
50
|
+
|
51
|
+
// instead Ruby assoc
|
52
|
+
assoc: function(arr, value) {
|
53
|
+
var i;
|
54
|
+
for (i = 0; i < arr.length; i += 1) {
|
55
|
+
if (arr[i][0] === value) { return arr[i][1]; }
|
56
|
+
}
|
57
|
+
},
|
58
|
+
|
59
|
+
// instead Ruby rassoc
|
60
|
+
rassoc: function(arr, value) {
|
61
|
+
var i;
|
62
|
+
for (i = 0; i < arr.length; i += 1) {
|
63
|
+
if (arr[i][1] === value) { return arr[i][0]; }
|
64
|
+
}
|
65
|
+
},
|
66
|
+
|
67
|
+
tassoc: function(arr, value) {
|
68
|
+
var i;
|
69
|
+
for (i = 0; i < arr.length; i += 1) {
|
70
|
+
if (arr[i][1] === value) { return arr[i][2]; }
|
71
|
+
}
|
72
|
+
},
|
73
|
+
|
74
|
+
// 3 functions instead Ruby actions with big integer (128 bits)
|
75
|
+
// addition
|
76
|
+
addHexStr: function(fnum, snum) {
|
77
|
+
var
|
78
|
+
delta = 0,
|
79
|
+
result = '',
|
80
|
+
i, sum;
|
81
|
+
|
82
|
+
for (i = fnum.length - 1; i >= 0; i -= 1) {
|
83
|
+
sum = parseInt(fnum.charAt(i), 16) + parseInt(snum.charAt(i), 16) + delta;
|
84
|
+
delta = 0;
|
85
|
+
|
86
|
+
if (sum > 15) {
|
87
|
+
delta = 1;
|
88
|
+
sum -= 16;
|
89
|
+
}
|
90
|
+
result = (sum).toString(16) + result;
|
91
|
+
}
|
92
|
+
return result;
|
93
|
+
},
|
94
|
+
|
95
|
+
// subtraction
|
96
|
+
subHexStr: function(fnum, snum) {
|
97
|
+
var
|
98
|
+
delta = 0,
|
99
|
+
result = '',
|
100
|
+
i, sum;
|
101
|
+
|
102
|
+
for (i = fnum.length - 1; i >= 0; i -= 1) {
|
103
|
+
sum = parseInt(fnum.charAt(i), 16) - parseInt(snum.charAt(i), 16) - delta;
|
104
|
+
delta = 0;
|
105
|
+
|
106
|
+
if (sum < 0) {
|
107
|
+
delta = 1;
|
108
|
+
sum += 16;
|
109
|
+
}
|
110
|
+
result = (sum).toString(16) + result;
|
111
|
+
}
|
112
|
+
return result;
|
113
|
+
},
|
114
|
+
|
115
|
+
// removing the leading zeros
|
116
|
+
cutHexStr: function(str) {
|
117
|
+
var i;
|
118
|
+
for (i = 0; i < str.length; i += 1) {
|
119
|
+
if (str.charAt(i) !== '0') {break; }
|
120
|
+
}
|
121
|
+
return str.substr(i, str.length - i);
|
122
|
+
},
|
123
|
+
|
124
|
+
//transform string of valid characters to useful array (del = true if we need delimiter)
|
125
|
+
alptoArr: function(alpStr, del) {
|
126
|
+
var
|
127
|
+
alpArr = [],
|
128
|
+
el = alpStr.length,
|
129
|
+
charItem,
|
130
|
+
pow = binPow(el - 1), // max number of bits coding by one character (some characters will be one bit less)
|
131
|
+
lowhi = (1 << pow) - el; // how many characters will be one bit less
|
132
|
+
|
133
|
+
if (del) { // if delimited we can't use last characters so recalculate variables
|
134
|
+
el -= 1;
|
135
|
+
pow = binPow(el - 1);
|
136
|
+
lowhi = (1 << pow) - el;
|
137
|
+
if (lowhi === 0) { lowhi -= 1; }
|
138
|
+
alpArr[alpArr.length] = [lowhi, alpStr.charAt(el), pow]; // first element include main data about alphabet and delimiter character
|
139
|
+
if (lowhi === -1) { lowhi += 1; }
|
140
|
+
} else {
|
141
|
+
if (lowhi === 0) { lowhi -= 1; }
|
142
|
+
alpArr[alpArr.length] = [lowhi, '', pow]; // first element include main data about alphabet
|
143
|
+
if (lowhi === -1) { lowhi += 1; }
|
144
|
+
}
|
145
|
+
|
146
|
+
for (charItem = 0; charItem < el; charItem += 1) { // loop by characters and get code and bit number for each one
|
147
|
+
if (charItem < lowhi) {
|
148
|
+
alpArr[alpArr.length] = [charItem, alpStr.charAt(charItem), pow - 1];
|
149
|
+
} else {
|
150
|
+
alpArr[alpArr.length] = [lowhi + charItem, alpStr.charAt(charItem), pow];
|
151
|
+
}
|
152
|
+
}
|
153
|
+
return alpArr;
|
154
|
+
},
|
155
|
+
|
156
|
+
// compress UUIDs array
|
157
|
+
alpCompress: function(arr, alpStr, order) {
|
158
|
+
// declare starting values
|
159
|
+
var
|
160
|
+
alpArr = alptoArr(alpStr, false), // get alphabet array without delimiter
|
161
|
+
nresult = '', // without delta we starting with only one bit
|
162
|
+
dresult = alpArr[alpArr.length - 1][1], // with delta we starting with delimiter (last character in alphabet, always has code of all ones)
|
163
|
+
prev = ('0').repeat(UNIT_LEN), // previous UUID for compress with delta (at start is zero)
|
164
|
+
next, // next UUID for compress with delta
|
165
|
+
pow = alpArr[0][2],
|
166
|
+
lowhi = alpArr[0][0],
|
167
|
+
achr = 0, // first bit equal 0 means we compress without delta
|
168
|
+
rest = 1,
|
169
|
+
item, // current UUID
|
170
|
+
i, // counters for loop
|
171
|
+
j,
|
172
|
+
curr, // buffer for bits of current character
|
173
|
+
powC, // number of bits of current character
|
174
|
+
code; // the code of current compressed symbol
|
175
|
+
|
176
|
+
// compress without delta
|
177
|
+
for (i = 0; i < arr.length; i += 1) { // loop by UUIDs
|
178
|
+
|
179
|
+
item = arr[i].replace(new RegExp('-', 'g'), ''); // remove '-' characters from UUID
|
180
|
+
|
181
|
+
for (j = item.length - 1; j >= 0; j -= 1) { // loops by UUID characters
|
182
|
+
|
183
|
+
curr = parseInt(item.charAt(j), 16); // get base binary code (BBC)
|
184
|
+
achr += (curr << rest);
|
185
|
+
rest += 4; // add BBC length
|
186
|
+
|
187
|
+
while (rest >= pow) { // create symbols to compressed string
|
188
|
+
powC = pow - 1; // try with a short symbol length
|
189
|
+
code = parseInt(revString(((achr & ((1 << powC) - 1)) + (1 << powC)).toString(2)), 2) >> 1;
|
190
|
+
|
191
|
+
if (code >= lowhi) {powC += 1; } // if we get code of long length symbols
|
192
|
+
|
193
|
+
rest -= powC; // decrease BBC length
|
194
|
+
code = parseInt(revString(((achr & ((1 << powC) - 1)) + (1 << powC)).toString(2)), 2) >> 1; // get reverse bits from the end of BBC to create new symbol
|
195
|
+
nresult += assoc(alpArr, code); // add new symbol
|
196
|
+
achr >>= powC; // remove used bits from BBC
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
if (rest > 0) { // check if we have tail of BBC
|
202
|
+
code = parseInt(revString(((achr & ((1 << rest) - 1)) + (1 << rest)).toString(2)), 2) >> 1; // get reverse bits of BBC to create new symbol
|
203
|
+
code <<= (pow - rest - 1); // add zeros to get valid symbol code
|
204
|
+
|
205
|
+
if (code >= lowhi) { code <<= 1; } // if we get code of long length symbols
|
206
|
+
|
207
|
+
nresult += assoc(alpArr, code); // add tail symbol
|
208
|
+
}
|
209
|
+
// compress with delta
|
210
|
+
arr.sort();
|
211
|
+
alpArr = alptoArr(alpStr, true); // get alphabet array with delimiter
|
212
|
+
pow = alpArr[0][2];
|
213
|
+
|
214
|
+
if ((pow > 1) && (!order)) { // we can't operate single symbol alphabet and not need to calculate delta if we keep order
|
215
|
+
|
216
|
+
lowhi = alpArr[0][0];
|
217
|
+
|
218
|
+
for (i = 0; i < arr.length; i += 1) { // loop by UUIDs
|
219
|
+
achr = 0;
|
220
|
+
rest = 0;
|
221
|
+
next = arr[i].replace(new RegExp('-', 'g'), ''); // remove '-' characters from UUID
|
222
|
+
item = subHexStr(next, prev); // calculate delta
|
223
|
+
prev = next;
|
224
|
+
|
225
|
+
if ((cutHexStr(item).length - 1) * 4 + strPow(cutHexStr(item).charAt(0)) < UNIT_LEN * 4 - pow) {
|
226
|
+
item = cutHexStr(item);
|
227
|
+
} // we use delimiter only if delta is less than UUID length minus one char
|
228
|
+
|
229
|
+
for (j = item.length - 1; j >= 0; j -= 1) { // loop by delta characters
|
230
|
+
curr = parseInt(item.charAt(j), 16); // get BBC
|
231
|
+
achr += (curr << rest);
|
232
|
+
|
233
|
+
if (j === 0 && item.length < UNIT_LEN) { // get BBC length (for the first character without leading zeros)
|
234
|
+
rest += strPow(item.charAt(0));
|
235
|
+
} else {
|
236
|
+
rest += 4;
|
237
|
+
}
|
238
|
+
while (rest >= pow) { // create symbols to compressed string
|
239
|
+
powC = pow - 1; // try with a short symbol length
|
240
|
+
code = parseInt(revString(((achr & ((1 << powC) - 1)) + (1 << powC)).toString(2)), 2) >> 1;
|
241
|
+
|
242
|
+
if (code >= lowhi) { powC += 1; } // if we get code of long length symbols
|
243
|
+
|
244
|
+
rest -= powC; // decrease BBC length
|
245
|
+
code = parseInt(revString(((achr & ((1 << powC) - 1)) + (1 << powC)).toString(2)), 2) >> 1; // get reverse bits from the end of BBC to create new symbol
|
246
|
+
dresult += assoc(alpArr, code); // add new symbol
|
247
|
+
achr >>= powC; // remove used bits from BBC
|
248
|
+
}
|
249
|
+
}
|
250
|
+
if (rest > 0) { // check if we have tail of BBC for current UUID
|
251
|
+
code = parseInt(revString(((achr & ((1 << rest) - 1)) + (1 << rest)).toString(2)), 2) >> 1; // try with a short symbol length
|
252
|
+
code <<= (pow - rest - 1); // add zeros to get valid symbol code
|
253
|
+
|
254
|
+
if (code >= lowhi) { code <<= 1; } // if we get code of long length symbols
|
255
|
+
|
256
|
+
dresult += assoc(alpArr, code); // add tail symbol for current UUID
|
257
|
+
}
|
258
|
+
|
259
|
+
if (item.length < UNIT_LEN) {dresult += alpArr[0][1]; } // add delimiter if we use less symbols than for whole UUID
|
260
|
+
}
|
261
|
+
|
262
|
+
} else {
|
263
|
+
|
264
|
+
order = true; // for single symbol alphabet we can choose only nresult
|
265
|
+
}
|
266
|
+
|
267
|
+
if ((dresult.length < nresult.length) && (!order)) { nresult = dresult; } // get better result or non delta if we need to keep order
|
268
|
+
|
269
|
+
return nresult;
|
270
|
+
},
|
271
|
+
|
272
|
+
// decompress UUIDs array
|
273
|
+
alpDecompress: function (str, alpStr) {
|
274
|
+
// declare starting values
|
275
|
+
var
|
276
|
+
result = [],
|
277
|
+
alpArr = alptoArr(alpStr, false), // get alphabet array without delimiter
|
278
|
+
pow = alpArr[0][2],
|
279
|
+
lowhi = alpArr[0][0],
|
280
|
+
achr = 0, // BBC
|
281
|
+
rest = 0, // BBC length
|
282
|
+
item = '', // buffer for UUIDs characters
|
283
|
+
curr, // current UUID
|
284
|
+
prev = ('0').repeat(UNIT_LEN), // previous UUID for decompress with delta (at start is zero)
|
285
|
+
i, // counter for loop
|
286
|
+
code, // BBC bits of current character
|
287
|
+
firstBit = true; // for the first bit removing if delta was not used
|
288
|
+
|
289
|
+
if ((rassoc(alpArr, str.charAt(0)) & (1 << (tassoc(alpArr, str.charAt(0)) - 1))) === 0) { // check first bit to choose if delta used when compress
|
290
|
+
|
291
|
+
// delta was not used
|
292
|
+
for (i = 0; i < str.length; i += 1) { // loop by symbols of compressed string
|
293
|
+
|
294
|
+
code = parseInt(revString((rassoc(alpArr, str.charAt(i)) + (1 << tassoc(alpArr, str.charAt(i)))).toString(2)), 2) >> 1; // reverse symbol code to BBC bits
|
295
|
+
achr += code << rest; // add bits to BBC
|
296
|
+
rest += tassoc(alpArr, str.charAt(i)); // add BBC length
|
297
|
+
|
298
|
+
if (firstBit) { // first bit processing
|
299
|
+
firstBit = false;
|
300
|
+
achr >>= 1;
|
301
|
+
rest -= 1;
|
302
|
+
}
|
303
|
+
|
304
|
+
while (rest >= 4) { // add new UUID caracters to buffer
|
305
|
+
rest -= 4; // decrease BBC length
|
306
|
+
item = (achr & 15).toString(16) + item; // add new UUID character
|
307
|
+
achr >>= 4; // remove used bits from BBC
|
308
|
+
}
|
309
|
+
|
310
|
+
if (item.length >= UNIT_LEN) { // if we get buffer with length equal or more than whole UUID
|
311
|
+
curr = item.substr(item.length - UNIT_LEN, UNIT_LEN); // extract UUID from the end of buffer
|
312
|
+
if (UNIT_LEN === 32) { // add '-' characters if we work with UUID
|
313
|
+
curr = curr.substr(0, 8) + '-' + curr.substr(8, 4) + '-' + curr.substr(12, 4) + '-' + curr.substr(16, 4) + '-' + curr.substr(20, 12);
|
314
|
+
}
|
315
|
+
result[result.length] = curr; // add new UUID to array
|
316
|
+
item = item.substr(0, item.length - UNIT_LEN); // remove used characters from buffer
|
317
|
+
}
|
318
|
+
|
319
|
+
}
|
320
|
+
} else {
|
321
|
+
|
322
|
+
// delta was used
|
323
|
+
alpArr = alptoArr(alpStr, true); // get alphabet array with delimiter
|
324
|
+
pow = alpArr[0][2];
|
325
|
+
lowhi = alpArr[0][0];
|
326
|
+
|
327
|
+
for (i = 1; i <= str.length; i += 1) { // loop by symbols of compressed string from second (the first is header) to next after last (for final buffer processing)
|
328
|
+
if ((str.charAt(i) === alpArr[0][1]) || (item.length >= UNIT_LEN)) { // we catch delimiter or we get buffer length equal or more than whole UUID
|
329
|
+
if (item.length >= UNIT_LEN) { // if buffer length than we need to look at the current symbol one more time (this pass we will not process it)
|
330
|
+
i -= 1;
|
331
|
+
item = item.substr(item.length - UNIT_LEN); // extract delta from the end of buffer
|
332
|
+
} else {
|
333
|
+
item = ('0').repeat(UNIT_LEN - item.length) + item; // if delimiter we add first zero characters to get whole UUID
|
334
|
+
}
|
335
|
+
|
336
|
+
curr = addHexStr(item, prev); // calculate UUID from delta
|
337
|
+
prev = curr;
|
338
|
+
|
339
|
+
if (UNIT_LEN === 32) { // add '-' characters if we work with UUID
|
340
|
+
curr = curr.substr(0, 8) + '-' + curr.substr(8, 4) + '-' + curr.substr(12, 4) + '-' + curr.substr(16, 4) + '-' + curr.substr(20, 12);
|
341
|
+
}
|
342
|
+
|
343
|
+
result[result.length] = curr; // add new UUID to array
|
344
|
+
achr = 0; // clear BBC and buffer
|
345
|
+
rest = 0;
|
346
|
+
item = '';
|
347
|
+
|
348
|
+
} else {
|
349
|
+
|
350
|
+
if (i < str.length) { // if we become last symbol we need no to symbol processing
|
351
|
+
|
352
|
+
code = parseInt(revString((rassoc(alpArr, str.charAt(i)) + (1 << tassoc(alpArr, str.charAt(i)))).toString(2)), 2) >> 1; // reverse symbol code to BBC bits
|
353
|
+
achr += code << rest; // add bits to BBC
|
354
|
+
rest += pow; // get number of bits in BBC
|
355
|
+
|
356
|
+
if (code < lowhi) { rest -= 1; }
|
357
|
+
|
358
|
+
while (rest >= 4) { // add new UUID caracters to buffer
|
359
|
+
rest -= 4; // decrease number of bits in BBC
|
360
|
+
item = (achr & 15).toString(16) + item; // add new UUID character
|
361
|
+
achr >>= 4; // remove used bits from BBC
|
362
|
+
}
|
363
|
+
}
|
364
|
+
}
|
365
|
+
}
|
366
|
+
}
|
367
|
+
return result;
|
368
|
+
}
|
369
|
+
|
370
|
+
};
|
371
|
+
|
372
|
+
}));
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "uuid_pack"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/id_pack.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'id_pack/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "id_pack"
|
9
|
+
spec.version = IdPack::VERSION
|
10
|
+
spec.authors = ["Ribose Inc."]
|
11
|
+
spec.email = ["open.source@ribose.com"]
|
12
|
+
|
13
|
+
spec.summary = 'Compression and packing methods for singular and collection IDs and UUIDs.'
|
14
|
+
spec.description = 'Compression and packing methods for singular and collection IDs and UUIDs.'
|
15
|
+
spec.homepage = "https://www.ribose.com"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
20
|
+
# if spec.respond_to?(:metadata)
|
21
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
22
|
+
# else
|
23
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
24
|
+
# "public gem pushes."
|
25
|
+
# end
|
26
|
+
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
28
|
+
f.match(%r{^(test|spec|features)/})
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
36
|
+
spec.add_development_dependency "rspec", "~> 3.6"
|
37
|
+
end
|