id_pack 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|