id_pack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
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
@@ -0,0 +1,3 @@
1
+ module IdPack
2
+ class Engine < ::Rails::Engine; end
3
+ end