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.
@@ -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