splitclient-rb 4.4.0 → 4.5.1.pre.dev

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e90a30df4c1f1e86befc5572851f37cee1e22eec
4
- data.tar.gz: 8b7eeb1065058af7efe9ec9b104d4b0bd589d199
3
+ metadata.gz: 35f1c408826430c3833af34b39b302e005b0e3d3
4
+ data.tar.gz: ee0fa5c89c7cb7a30dd0a18fa63dc645673170d9
5
5
  SHA512:
6
- metadata.gz: da5a3fc027744717443eb00c87615444890b5cccc6c018baf89f93bd111fba8951ca1f64bdab839eead72e907befee279727402961dfb56dd1e02d8d410bc046
7
- data.tar.gz: a86cd66443680a28c0b207f08cf8ff0c8e0e3130ecd9c77f542a419209c0786b3cbfe4fea4d3dc6d4afb07acb7344ac335d9478834f0a063ca3fc2e01141f910
6
+ metadata.gz: a324c37b240280be2de970a9f028095bb27cacf59bfc41c8408c509c19997933cfa90211953856c2840df9c9683e128c281b3ba33c8a2296d236cb1d97f920bc
7
+ data.tar.gz: a129c6547c71383840c78e5520d4ce544a483bea9eb5b05c09d7c1e87dfdc79b141afe0c73645e4f5b861adafd18230a341d095005cc1e455d63d0053e2b98d6
data/.gitignore CHANGED
@@ -36,3 +36,10 @@ Gemfile.lock
36
36
 
37
37
  # Ignore Byebug command history file.
38
38
  .byebug_history
39
+
40
+ # Ignore built extensions
41
+ lib/murmurhash/murmurhash.bundle
42
+ lib/murmurhash/murmurhash.so
43
+
44
+ ext/murmurhash/murmurhash.bundle
45
+ ext/murmurhash/murmurhash.so
data/CHANGES.txt CHANGED
@@ -1,3 +1,12 @@
1
+ 4.5.1 (Mar 23rd, 2018)
2
+ - Fix Forwardable load issue
3
+ - Fix native extension path issue
4
+
5
+ 4.5.0 (Mar 2nd, 2018)
6
+
7
+ - Move MurmurHash3 implementation inside the gem
8
+ - Add native Java MurmurHash3 implementation -> now support JRuby
9
+
1
10
  4.4.0 (Feb 5th, 2018)
2
11
  - Add track API
3
12
 
data/NEWS CHANGED
@@ -1,3 +1,7 @@
1
+ 4.5.0
2
+
3
+ Add JRuby support
4
+
1
5
  4.4.0
2
6
 
3
7
  Add the ability to send event data to split service (.track() api)
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [ ![Codeship Status for splitio/ruby-client](https://app.codeship.com/projects/306c6b60-c164-0133-e179-16471d4e6045/status?branch=master)](https://app.codeship.com/projects/137510)
2
+
1
3
  # Split Ruby SDK
2
4
 
3
5
  This SDK is designed to work with [Split](https://www.split.io), the platform for controlled rollouts, serving features to your users via the Split feature flag to manage your complete customer experience.
data/Rakefile CHANGED
@@ -1,4 +1,24 @@
1
- require "bundler/gem_tasks"
2
- task :default => :spec
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
3
7
 
4
- Dir.glob('tasks/**/*.rake').each(&method(:import))
8
+ task :spec => :compile
9
+ case RUBY_PLATFORM
10
+ when 'java'
11
+ require 'rake/javaextensiontask'
12
+ Rake::JavaExtensionTask.new 'murmurhash' do |ext|
13
+ ext.lib_dir = 'lib/murmurhash'
14
+ ext.target_version = '1.7'
15
+ ext.source_version = '1.7'
16
+ end
17
+ else
18
+ require 'rake/extensiontask'
19
+ Rake::ExtensionTask.new 'murmurhash' do |ext|
20
+ ext.lib_dir = 'lib/murmurhash'
21
+ end
22
+ end
23
+
24
+ task :default => :spec
@@ -0,0 +1,88 @@
1
+ /*
2
+ * MurmurHash3_x86_32 (C) Austin Appleby
3
+ */
4
+
5
+ #include "murmurhash.h"
6
+
7
+ uint32_t
8
+ murmur_hash_process3_x86_32(const char * key, uint32_t len, uint32_t seed)
9
+ {
10
+ const uint8_t * data = (const uint8_t*)key;
11
+ const int nblocks = len / 4;
12
+ int i;
13
+
14
+ uint32_t h1 = seed;
15
+
16
+ const uint32_t c1 = 0xcc9e2d51;
17
+ const uint32_t c2 = 0x1b873593;
18
+
19
+ //----------
20
+ // body
21
+
22
+ const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
23
+
24
+ for(i = -nblocks; i; i++)
25
+ {
26
+ uint32_t k1 = getblock32(blocks,i);
27
+
28
+ k1 *= c1;
29
+ k1 = ROTL32(k1,15);
30
+ k1 *= c2;
31
+
32
+ h1 ^= k1;
33
+ h1 = ROTL32(h1,13);
34
+ h1 = h1*5+0xe6546b64;
35
+ }
36
+
37
+ //----------
38
+ // tail
39
+
40
+ const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
41
+
42
+ uint32_t k1 = 0;
43
+
44
+ switch(len & 3)
45
+ {
46
+ case 3: k1 ^= tail[2] << 16;
47
+ case 2: k1 ^= tail[1] << 8;
48
+ case 1: k1 ^= tail[0];
49
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
50
+ };
51
+
52
+ //----------
53
+ // finalization
54
+
55
+ h1 ^= len;
56
+
57
+ h1 = fmix32(h1);
58
+
59
+ return h1;
60
+ }
61
+
62
+ VALUE
63
+ murmur3_x86_32_finish(VALUE self)
64
+ {
65
+ uint8_t digest[4];
66
+ uint32_t h;
67
+
68
+ h = _murmur_finish32(self, murmur_hash_process3_x86_32);
69
+ assign_by_endian_32(digest, h);
70
+ return rb_str_new((const char*) digest, 4);
71
+ }
72
+
73
+ VALUE
74
+ murmur3_x86_32_s_digest(int argc, VALUE *argv, VALUE klass)
75
+ {
76
+ uint8_t digest[4];
77
+ uint32_t h;
78
+
79
+ h = _murmur_s_digest32(argc, argv, klass, murmur_hash_process3_x86_32);
80
+ assign_by_endian_32(digest, h);
81
+ return rb_str_new((const char*) digest, 4);
82
+ }
83
+
84
+ VALUE
85
+ murmur3_x86_32_s_rawdigest(int argc, VALUE *argv, VALUE klass)
86
+ {
87
+ return ULL2NUM(_murmur_s_digest32(argc, argv, klass, murmur_hash_process3_x86_32));
88
+ }
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('murmurhash/murmurhash')
@@ -0,0 +1,251 @@
1
+ #include "murmurhash.h"
2
+
3
+ ID id_DEFAULT_SEED;
4
+ ID iv_seed;
5
+ ID iv_buffer;
6
+
7
+
8
+ inline uint32_t rotl32 ( uint32_t x, int8_t r )
9
+ {
10
+ return (x << r) | (x >> (32 - r));
11
+ }
12
+ inline uint64_t rotl64 ( uint64_t x, int8_t r )
13
+ {
14
+ return (x << r) | (x >> (64 - r));
15
+ }
16
+
17
+ FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
18
+ {
19
+ return p[i];
20
+ }
21
+
22
+ FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i )
23
+ {
24
+ return p[i];
25
+ }
26
+
27
+ FORCE_INLINE uint32_t fmix32 ( uint32_t h )
28
+ {
29
+ h ^= h >> 16;
30
+ h *= 0x85ebca6b;
31
+ h ^= h >> 13;
32
+ h *= 0xc2b2ae35;
33
+ h ^= h >> 16;
34
+
35
+ return h;
36
+ }
37
+
38
+ FORCE_INLINE uint64_t fmix64 ( uint64_t k )
39
+ {
40
+ k ^= k >> 33;
41
+ k *= BIG_CONSTANT(0xff51afd7ed558ccd);
42
+ k ^= k >> 33;
43
+ k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
44
+ k ^= k >> 33;
45
+
46
+ return k;
47
+ }
48
+
49
+ void
50
+ assign_by_endian_32(uint8_t *digest, uint32_t h)
51
+ {
52
+ if (BIGENDIAN_P()) {
53
+ digest[0] = h >> 24;
54
+ digest[1] = h >> 16;
55
+ digest[2] = h >> 8;
56
+ digest[3] = h;
57
+ }
58
+ else {
59
+ digest[3] = h >> 24;
60
+ digest[2] = h >> 16;
61
+ digest[1] = h >> 8;
62
+ digest[0] = h;
63
+ }
64
+ }
65
+
66
+ void
67
+ assign_by_endian_64(uint8_t *digest, uint64_t h)
68
+ {
69
+ if (BIGENDIAN_P()) {
70
+ digest[0] = h >> 56;
71
+ digest[1] = h >> 48;
72
+ digest[2] = h >> 40;
73
+ digest[3] = h >> 32;
74
+ digest[4] = h >> 24;
75
+ digest[5] = h >> 16;
76
+ digest[6] = h >> 8;
77
+ digest[7] = h;
78
+ }
79
+ else {
80
+ digest[7] = h >> 56;
81
+ digest[6] = h >> 48;
82
+ digest[5] = h >> 40;
83
+ digest[4] = h >> 32;
84
+ digest[3] = h >> 24;
85
+ digest[2] = h >> 16;
86
+ digest[1] = h >> 8;
87
+ digest[0] = h;
88
+ }
89
+ }
90
+
91
+ void
92
+ assign_by_endian_128(uint8_t *digest, void *out)
93
+ {
94
+ int i;
95
+
96
+ if (BIGENDIAN_P()) {
97
+ for (i = 0; i < 4; i++) {
98
+ digest[(i*4) ] = ((uint32_t*)out)[i] >> 24;
99
+ digest[(i*4)+1] = ((uint32_t*)out)[i] >> 16;
100
+ digest[(i*4)+2] = ((uint32_t*)out)[i] >> 8;
101
+ digest[(i*4)+3] = ((uint32_t*)out)[i];
102
+ }
103
+ }
104
+ else {
105
+ for (i = 0; i < 4; i++) {
106
+ digest[16-(i*4)-1] = ((uint32_t*)out)[i] >> 24;
107
+ digest[16-(i*4)-2] = ((uint32_t*)out)[i] >> 16;
108
+ digest[16-(i*4)-3] = ((uint32_t*)out)[i] >> 8;
109
+ digest[16-(i*4)-4] = ((uint32_t*)out)[i];
110
+ }
111
+ }
112
+ }
113
+
114
+ static uint32_t
115
+ rstring2uint32_t(VALUE str)
116
+ {
117
+ long len = RSTRING_LEN(str);
118
+ if (UINT32_MAX < len) {
119
+ rb_raise(rb_eRangeError, "String length=%ld will overflow from long to uint32_t", len);
120
+ }
121
+ return (uint32_t)len;
122
+ }
123
+
124
+ uint32_t
125
+ _murmur_finish32(VALUE self, uint32_t (*process)(const char*, uint32_t, uint32_t))
126
+ {
127
+ const char *seed = RSTRING_PTR(rb_ivar_get(self, iv_seed));
128
+ VALUE buffer = rb_ivar_get(self, iv_buffer);
129
+ return process(RSTRING_PTR(buffer), rstring2uint32_t(buffer), *(uint32_t*)seed);
130
+ }
131
+
132
+ uint64_t
133
+ _murmur_finish64(VALUE self, uint64_t (*process)(const char*, uint32_t, uint64_t))
134
+ {
135
+ const char *seed = RSTRING_PTR(rb_ivar_get(self, iv_seed));
136
+ VALUE buffer = rb_ivar_get(self, iv_buffer);
137
+ return process(RSTRING_PTR(buffer), rstring2uint32_t(buffer), *(uint64_t*)seed);
138
+ }
139
+
140
+ void
141
+ _murmur_finish128(VALUE self, void *out, void (*process)(const char*, uint32_t, uint32_t, void*))
142
+ {
143
+ const char *seed = RSTRING_PTR(rb_ivar_get(self, iv_seed));
144
+ VALUE buffer = rb_ivar_get(self, iv_buffer);
145
+ process(RSTRING_PTR(buffer), rstring2uint32_t(buffer), *(uint32_t*)seed, out);
146
+ }
147
+
148
+ uint32_t
149
+ _murmur_s_digest32(int argc, VALUE *argv, VALUE klass, uint32_t (*process)(const char *, uint32_t, uint32_t))
150
+ {
151
+ VALUE str;
152
+ const char *seed;
153
+
154
+ if (argc < 1)
155
+ rb_raise(rb_eArgError, "no data given");
156
+
157
+ str = *argv;
158
+
159
+ StringValue(str);
160
+
161
+ if (1 < argc) {
162
+ StringValue(argv[1]);
163
+ if (RSTRING_LEN(argv[1]) != 4) {
164
+ rb_raise(rb_eArgError, "seed string should be 4 length");
165
+ }
166
+ seed = RSTRING_PTR(argv[1]);
167
+ } else {
168
+ seed = RSTRING_PTR(rb_const_get(klass, id_DEFAULT_SEED));
169
+ }
170
+
171
+ return process(RSTRING_PTR(str), rstring2uint32_t(str), *(uint32_t*)seed);
172
+ }
173
+
174
+ uint64_t
175
+ _murmur_s_digest64(int argc, VALUE *argv, VALUE klass, uint64_t (*process)(const char *, uint32_t, uint64_t))
176
+ {
177
+ VALUE str;
178
+ const char *seed;
179
+
180
+ if (argc < 1)
181
+ rb_raise(rb_eArgError, "no data given");
182
+
183
+ str = *argv;
184
+
185
+ StringValue(str);
186
+
187
+ if (1 < argc) {
188
+ StringValue(argv[1]);
189
+ if (RSTRING_LEN(argv[1]) != 8) {
190
+ rb_raise(rb_eArgError, "seed string should be 8 length");
191
+ }
192
+ seed = RSTRING_PTR(argv[1]);
193
+ } else {
194
+ seed = RSTRING_PTR(rb_const_get(klass, id_DEFAULT_SEED));
195
+ }
196
+
197
+ return process(RSTRING_PTR(str), rstring2uint32_t(str), *(uint64_t*)seed);
198
+ }
199
+
200
+ void
201
+ _murmur_s_digest128(int argc, VALUE *argv, VALUE klass, void *out, void (*process)(const char *, uint32_t, uint32_t, void *))
202
+ {
203
+ VALUE str;
204
+ const char *seed;
205
+ int seed_length = 4;
206
+
207
+ if (argc < 1)
208
+ rb_raise(rb_eArgError, "no data given");
209
+
210
+ str = *argv;
211
+
212
+ StringValue(str);
213
+
214
+ if (1 < argc) {
215
+ StringValue(argv[1]);
216
+ if (RSTRING_LEN(argv[1]) != seed_length) {
217
+ rb_raise(rb_eArgError, "seed string should be %d length", seed_length);
218
+ }
219
+ seed = RSTRING_PTR(argv[1]);
220
+ } else {
221
+ seed = RSTRING_PTR(rb_const_get(klass, id_DEFAULT_SEED));
222
+ }
223
+
224
+ process(RSTRING_PTR(str), rstring2uint32_t(str), *(uint32_t*)seed, out);
225
+ }
226
+
227
+
228
+ void
229
+ Init_murmurhash(void)
230
+ {
231
+ VALUE cDigest_MurmurHash1,
232
+ cDigest_MurmurHash2,
233
+ cDigest_MurmurHash2A,
234
+ cDigest_MurmurHash64A,
235
+ cDigest_MurmurHash64B,
236
+ cDigest_MurmurHashNeutral2,
237
+ cDigest_MurmurHashAligned2,
238
+ cDigest_MurmurHash3_x86_32,
239
+ cDigest_MurmurHash3_x86_128,
240
+ cDigest_MurmurHash3_x64_128;
241
+
242
+ id_DEFAULT_SEED = rb_intern("DEFAULT_SEED");
243
+ iv_seed = rb_intern("@seed");
244
+ iv_buffer = rb_intern("@buffer");
245
+
246
+
247
+ cDigest_MurmurHash3_x86_32 = rb_path2class("Digest::MurmurHashMRI3_x86_32");
248
+ rb_define_singleton_method(cDigest_MurmurHash3_x86_32, "digest", murmur3_x86_32_s_digest, -1);
249
+ rb_define_singleton_method(cDigest_MurmurHash3_x86_32, "rawdigest", murmur3_x86_32_s_rawdigest, -1);
250
+ rb_define_private_method(cDigest_MurmurHash3_x86_32, "finish", murmur3_x86_32_finish, 0);
251
+ }
@@ -0,0 +1,94 @@
1
+ #ifndef MURMURHASH_INCLUDED
2
+ # define MURMURHASH_INCLUDED
3
+
4
+ #include "ruby.h"
5
+
6
+ // Microsoft Visual Studio
7
+
8
+ #if defined(_MSC_VER)
9
+ #define FORCE_INLINE __forceinline
10
+ #include <stdlib.h>
11
+ #define ROTL32(x,y) _rotl(x,y)
12
+ #define ROTL64(x,y) _rotl64(x,y)
13
+ #define BIG_CONSTANT(x) (x)
14
+ #else // defined(_MSC_VER)
15
+ #define FORCE_INLINE inline __attribute__((always_inline))
16
+ #define ROTL32(x,y) rotl32(x,y)
17
+ #define ROTL64(x,y) rotl64(x,y)
18
+ #define BIG_CONSTANT(x) (x##LLU)
19
+ #endif // !defined(_MSC_VER)
20
+
21
+ #ifdef DYNAMIC_ENDIAN
22
+ /* for universal binary of NEXTSTEP and MacOS X */
23
+ /* useless since autoconf 2.63? */
24
+ static int
25
+ is_bigendian(void)
26
+ {
27
+ static int init = 0;
28
+ static int endian_value;
29
+ char *p;
30
+
31
+ if (init) return endian_value;
32
+ init = 1;
33
+ p = (char*)&init;
34
+ return endian_value = p[0] ? 0 : 1;
35
+ }
36
+ # define BIGENDIAN_P() (is_bigendian())
37
+ #elif defined(WORDS_BIGENDIAN)
38
+ # define BIGENDIAN_P() 1
39
+ #else
40
+ # define BIGENDIAN_P() 0
41
+ #endif
42
+
43
+ #define MURMURHASH_MAGIC 0x5bd1e995
44
+ #define MURMURHASH_MAGIC64A BIG_CONSTANT(0xc6a4a7935bd1e995)
45
+
46
+ void assign_by_endian_32(uint8_t *digest, uint32_t h);
47
+ void assign_by_endian_64(uint8_t *digest, uint64_t h);
48
+ void assign_by_endian_128(uint8_t*, void*);
49
+
50
+ uint32_t rotl32(uint32_t, int8_t);
51
+ uint64_t rotl64(uint64_t, int8_t);
52
+ uint32_t getblock32(const uint32_t*, int);
53
+ uint64_t getblock64(const uint64_t*, int);
54
+ uint32_t fmix32(uint32_t);
55
+ uint64_t fmix64(uint64_t);
56
+ uint32_t _murmur_finish32(VALUE, uint32_t (*)(const char*, uint32_t, uint32_t));
57
+ uint64_t _murmur_finish64(VALUE, uint64_t (*)(const char*, uint32_t, uint64_t));
58
+ void _murmur_finish128(VALUE, void*, void (*)(const char*, uint32_t, uint32_t, void*));
59
+ uint32_t _murmur_s_digest32(int, VALUE*, VALUE, uint32_t (*)(const char*, uint32_t, uint32_t));
60
+ uint64_t _murmur_s_digest64(int, VALUE*, VALUE, uint64_t (*)(const char*, uint32_t, uint64_t));
61
+ void _murmur_s_digest128(int, VALUE*, VALUE, void*, void (*)(const char*, uint32_t, uint32_t, void*));
62
+
63
+ VALUE murmur1_finish(VALUE);
64
+ VALUE murmur1_s_digest(int, VALUE*, VALUE);
65
+ VALUE murmur1_s_rawdigest(int, VALUE*, VALUE);
66
+ VALUE murmur2_finish(VALUE);
67
+ VALUE murmur2_s_digest(int, VALUE*, VALUE);
68
+ VALUE murmur2_s_rawdigest(int, VALUE*, VALUE);
69
+ VALUE murmur2a_finish(VALUE);
70
+ VALUE murmur2a_s_digest(int, VALUE*, VALUE);
71
+ VALUE murmur2a_s_rawdigest(int, VALUE*, VALUE);
72
+ VALUE murmur64a_finish(VALUE);
73
+ VALUE murmur64a_s_digest(int, VALUE*, VALUE);
74
+ VALUE murmur64a_s_rawdigest(int, VALUE*, VALUE);
75
+ VALUE murmur64b_finish(VALUE);
76
+ VALUE murmur64b_s_digest(int, VALUE*, VALUE);
77
+ VALUE murmur64b_s_rawdigest(int, VALUE*, VALUE);
78
+ VALUE murmur_neutral2_finish(VALUE);
79
+ VALUE murmur_neutral2_s_digest(int, VALUE*, VALUE);
80
+ VALUE murmur_neutral2_s_rawdigest(int, VALUE*, VALUE);
81
+ VALUE murmur_aligned2_finish(VALUE);
82
+ VALUE murmur_aligned2_s_digest(int, VALUE*, VALUE);
83
+ VALUE murmur_aligned2_s_rawdigest(int, VALUE*, VALUE);
84
+ VALUE murmur3_x86_32_finish(VALUE);
85
+ VALUE murmur3_x86_32_s_digest(int, VALUE*, VALUE);
86
+ VALUE murmur3_x86_32_s_rawdigest(int, VALUE*, VALUE);
87
+ VALUE murmur3_x86_128_finish(VALUE);
88
+ VALUE murmur3_x86_128_s_digest(int, VALUE*, VALUE);
89
+ VALUE murmur3_x86_128_s_rawdigest(int, VALUE*, VALUE);
90
+ VALUE murmur3_x64_128_finish(VALUE);
91
+ VALUE murmur3_x64_128_s_digest(int, VALUE*, VALUE);
92
+ VALUE murmur3_x64_128_s_rawdigest(int, VALUE*, VALUE);
93
+
94
+ #endif /* ifndef MURMURHASH_INCLUDED */
@@ -0,0 +1,58 @@
1
+ module Digest
2
+ ds = Struct.new(:digest_length, :seed_length)
3
+ s1 = ds.new(4, 4)
4
+ s2 = ds.new(8, 8)
5
+ s3 = ds.new(16, 4)
6
+ {
7
+ '1' => s1,
8
+ '2' => s1,
9
+ '2A' => s1,
10
+ '64A' => s2,
11
+ '64B' => s2,
12
+ 'Aligned2' => s1,
13
+ 'Neutral2' => s1,
14
+ '3_x86_32' => s1,
15
+ '3_x86_128' => s3,
16
+ '3_x64_128' => s3,
17
+ }.each do |name, s|
18
+ class_eval %Q{
19
+ class MurmurHashMRI#{name} < Digest::Class
20
+ DEFAULT_SEED = "#{"\x00" * s.seed_length}".b
21
+
22
+ def initialize
23
+ @buffer = ""
24
+ @seed = DEFAULT_SEED
25
+ end
26
+
27
+ def update(str)
28
+ @buffer << str
29
+ self
30
+ end
31
+ alias << update
32
+
33
+ def reset
34
+ @buffer.clear
35
+ @seed = DEFAULT_SEED
36
+ self
37
+ end
38
+
39
+ def seed
40
+ @seed
41
+ end
42
+
43
+ def seed=(s)
44
+ raise ArgumentError, "seed string should be #{s.seed_length} length" if #{s.seed_length} != s.length
45
+ @seed = s
46
+ end
47
+
48
+ def digest_length
49
+ #{s.digest_length}
50
+ end
51
+
52
+ def block_length
53
+ 0
54
+ end
55
+ end
56
+ }
57
+ end
58
+ end
Binary file
@@ -0,0 +1,3 @@
1
+ require 'digest'
2
+ require 'murmurhash/base'
3
+ require 'murmurhash/murmurhash'
@@ -1,5 +1,3 @@
1
- require 'forwardable'
2
-
3
1
  module SplitIoClient
4
2
  module Cache
5
3
  module Repositories
@@ -1,118 +1,123 @@
1
- require 'digest/murmurhash'
2
-
3
1
  module SplitIoClient
4
2
  # Misc class in charge of providing hash functions and
5
3
  # determination of treatment based on concept of buckets
6
4
  # based on provided key
7
5
  #
8
6
  class Splitter < NoMethodError
9
- class << self
10
- #
11
- # Checks if the partiotion size is 100%
12
- #
13
- # @param partitions [object] array of partitions
14
- #
15
- # @return [boolean] true if partition is 100% false otherwise
16
- def hundred_percent_one_treatment?(partitions)
17
- (partitions.size != 1) ? false : (partitions.first.size == 100)
7
+ def initialize
8
+ @murmur_hash = case RUBY_PLATFORM
9
+ when 'java'
10
+ Proc.new { |key, seed| Java::MurmurHash3.murmurhash3_x86_32(key, seed) }
11
+ else
12
+ Proc.new { |key, seed| Digest::MurmurHashMRI3_x86_32.rawdigest(key, [seed].pack('L')) }
18
13
  end
14
+ end
15
+
16
+ #
17
+ # Checks if the partiotion size is 100%
18
+ #
19
+ # @param partitions [object] array of partitions
20
+ #
21
+ # @return [boolean] true if partition is 100% false otherwise
22
+ def hundred_percent_one_treatment?(partitions)
23
+ (partitions.size != 1) ? false : (partitions.first.size == 100)
24
+ end
19
25
 
20
- #
21
- # gets the appropriate treatment based on id, seed and partition value
22
- #
23
- # @param id [string] user key
24
- # @param seed [number] seed for the user key
25
- # @param partitions [object] array of partitions
26
- #
27
- # @return traetment [object] treatment value
28
- def get_treatment(id, seed, partitions, legacy_algo)
29
- legacy = (legacy_algo == 1 || legacy_algo == nil) ? true : false
30
-
31
- if partitions.empty?
32
- return SplitIoClient::Engine::Models::Treatment::CONTROL
33
- end
34
-
35
- if hundred_percent_one_treatment?(partitions)
36
- return (partitions.first).treatment
37
- end
38
-
39
- return get_treatment_for_key(bucket(count_hash(id, seed, legacy_algo)), partitions)
26
+ #
27
+ # gets the appropriate treatment based on id, seed and partition value
28
+ #
29
+ # @param id [string] user key
30
+ # @param seed [number] seed for the user key
31
+ # @param partitions [object] array of partitions
32
+ #
33
+ # @return traetment [object] treatment value
34
+ def get_treatment(id, seed, partitions, legacy_algo)
35
+ legacy = [1, nil].include?(legacy_algo)
36
+
37
+ if partitions.empty?
38
+ return SplitIoClient::Engine::Models::Treatment::CONTROL
40
39
  end
41
40
 
42
- # returns a hash value for the give key, seed pair
43
- #
44
- # @param key [String] user key
45
- # @param seed [Fixnum] seed for the user key
46
- #
47
- # @return hash [String] hash value
48
- def count_hash(key, seed, legacy)
49
- legacy ? legacy_hash(key, seed) : murmur_hash(key, seed)
41
+ if hundred_percent_one_treatment?(partitions)
42
+ return (partitions.first).treatment
50
43
  end
51
44
 
52
- def murmur_hash(key, seed)
53
- Digest::MurmurHash3_x86_32.rawdigest(key, [seed].pack('L'))
54
- end
45
+ return get_treatment_for_key(bucket(count_hash(id, seed, legacy_algo)), partitions)
46
+ end
55
47
 
56
- def legacy_hash(key, seed)
57
- h = 0
48
+ # returns a hash value for the give key, seed pair
49
+ #
50
+ # @param key [String] user key
51
+ # @param seed [Fixnum] seed for the user key
52
+ #
53
+ # @return hash [String] hash value
54
+ def count_hash(key, seed, legacy)
55
+ legacy ? legacy_hash(key, seed) : murmur_hash(key, seed)
56
+ end
58
57
 
59
- for i in 0..key.length-1
60
- h = to_int32(31 * h + key[i].ord)
61
- end
58
+ def murmur_hash(key, seed)
59
+ @murmur_hash.call(key, seed)
60
+ end
62
61
 
63
- h^seed
64
- end
62
+ def legacy_hash(key, seed)
63
+ h = 0
65
64
 
66
- #
67
- # misc method to convert ruby number to int 32 since overflow is handled different to java
68
- #
69
- # @param number [number] ruby number value
70
- #
71
- # @return [int] returns the int 32 value of the provided number
72
- def to_int32(number)
73
- begin
74
- sign = number < 0 ? -1 : 1
75
- abs = number.abs
76
- return 0 if abs == 0 || abs == Float::INFINITY
77
- rescue
78
- return 0
79
- end
65
+ for i in 0..key.length-1
66
+ h = to_int32(31 * h + key[i].ord)
67
+ end
80
68
 
81
- pos_int = sign * abs.floor
82
- int_32bit = pos_int % 2**32
69
+ h^seed
70
+ end
83
71
 
84
- return int_32bit - 2**32 if int_32bit >= 2**31
85
- int_32bit
72
+ #
73
+ # misc method to convert ruby number to int 32 since overflow is handled different to java
74
+ #
75
+ # @param number [number] ruby number value
76
+ #
77
+ # @return [int] returns the int 32 value of the provided number
78
+ def to_int32(number)
79
+ begin
80
+ sign = number < 0 ? -1 : 1
81
+ abs = number.abs
82
+ return 0 if abs == 0 || abs == Float::INFINITY
83
+ rescue
84
+ return 0
86
85
  end
87
86
 
88
- #
89
- # returns the treatment for a bucket given the partitions
90
- #
91
- # @param bucket [number] bucket value
92
- # @param parittions [object] array of partitions
93
- #
94
- # @return treatment [treatment] treatment value for this bucket and partitions
95
- def get_treatment_for_key(bucket, partitions)
96
- buckets_covered_thus_far = 0
97
- partitions.each do |p|
98
- unless p.is_empty?
99
- buckets_covered_thus_far += p.size
100
- return p.treatment if buckets_covered_thus_far >= bucket
101
- end
102
- end
87
+ pos_int = sign * abs.floor
88
+ int_32bit = pos_int % 2**32
103
89
 
104
- return SplitIoClient::Engine::Models::Treatment::CONTROL
105
- end
90
+ return int_32bit - 2**32 if int_32bit >= 2**31
91
+ int_32bit
92
+ end
106
93
 
107
- #
108
- # returns bucket value for the given hash value
109
- #
110
- # @param hash_value [string] hash value
111
- #
112
- # @return bucket [number] bucket number
113
- def bucket(hash_value)
114
- (hash_value.abs % 100) + 1
94
+ #
95
+ # returns the treatment for a bucket given the partitions
96
+ #
97
+ # @param bucket [number] bucket value
98
+ # @param parittions [object] array of partitions
99
+ #
100
+ # @return treatment [treatment] treatment value for this bucket and partitions
101
+ def get_treatment_for_key(bucket, partitions)
102
+ buckets_covered_thus_far = 0
103
+ partitions.each do |p|
104
+ unless p.is_empty?
105
+ buckets_covered_thus_far += p.size
106
+ return p.treatment if buckets_covered_thus_far >= bucket
107
+ end
115
108
  end
109
+
110
+ return SplitIoClient::Engine::Models::Treatment::CONTROL
111
+ end
112
+
113
+ #
114
+ # returns bucket value for the given hash value
115
+ #
116
+ # @param hash_value [string] hash value
117
+ #
118
+ # @return bucket [number] bucket number
119
+ def bucket(hash_value)
120
+ (hash_value.abs % 100) + 1
116
121
  end
117
122
  end
118
123
  end
@@ -40,6 +40,7 @@ module SplitIoClient
40
40
  in_rollout = false
41
41
  key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
42
42
  legacy_algo = (split[:algo] == 1 || split[:algo] == nil) ? true : false
43
+ splitter = Splitter.new
43
44
 
44
45
  split[:conditions].each do |c|
45
46
  condition = SplitIoClient::Condition.new(c)
@@ -48,7 +49,7 @@ module SplitIoClient
48
49
 
49
50
  if !in_rollout && condition.type == SplitIoClient::Condition::TYPE_ROLLOUT
50
51
  if split[:trafficAllocation] < 100
51
- bucket = Splitter.bucket(Splitter.count_hash(key, split[:trafficAllocationSeed].to_i, legacy_algo))
52
+ bucket = splitter.bucket(splitter.count_hash(key, split[:trafficAllocationSeed].to_i, legacy_algo))
52
53
 
53
54
  if bucket >= split[:trafficAllocation]
54
55
  return treatment_hash(Models::Label::NOT_IN_SPLIT, split[:defaultTreatment], split[:changeNumber])
@@ -67,7 +68,7 @@ module SplitIoClient
67
68
 
68
69
  next unless condition_matched
69
70
 
70
- result = Splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
71
+ result = splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
71
72
 
72
73
  if result.nil?
73
74
  return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber])
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '4.4.0'
2
+ VERSION = '4.5.1-dev'
3
3
  end
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  require 'splitclient-rb/version'
2
4
 
3
5
  require 'splitclient-rb/exceptions/impressions_shutdown_exception'
@@ -78,6 +80,9 @@ require 'splitclient-rb/engine/models/label'
78
80
  require 'splitclient-rb/engine/models/treatment'
79
81
  require 'splitclient-rb/utilitites'
80
82
 
83
+ # C extension
84
+ require 'murmurhash/murmurhash_mri'
85
+
81
86
  module SplitIoClient
82
87
  def self.root
83
88
  File.dirname(__dir__)
@@ -14,18 +14,30 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/splitio/ruby-client"
15
15
  spec.license = "Apache 2.0"
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|features|ext)/}) }
18
+
18
19
  spec.bindir = "exe"
19
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
21
  spec.require_paths = ["lib"]
21
22
 
23
+ if defined?(JRUBY_VERSION)
24
+ spec.platform = 'java'
25
+ spec.files << 'ext/murmurhash/MurmurHash3.java'
26
+ else
27
+ spec.files.concat(%w(
28
+ ext/murmurhash/3_x86_32.c
29
+ ext/murmurhash/extconf.rb
30
+ ext/murmurhash/murmurhash.c
31
+ ext/murmurhash/murmurhash.h)
32
+ )
33
+ spec.extensions = ["ext/murmurhash/extconf.rb"]
34
+ end
35
+
22
36
  spec.add_development_dependency "bundler", "~> 1.11"
23
37
  spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rake-compiler"
24
39
  spec.add_development_dependency "rspec"
25
40
  spec.add_development_dependency "webmock"
26
- spec.add_development_dependency "byebug"
27
- spec.add_development_dependency "pry"
28
- spec.add_development_dependency "pry-byebug"
29
41
  spec.add_development_dependency "simplecov"
30
42
  spec.add_development_dependency "allocation_stats"
31
43
 
@@ -35,5 +47,4 @@ Gem::Specification.new do |spec|
35
47
  spec.add_runtime_dependency "faraday", ">= 0.8"
36
48
  spec.add_runtime_dependency "net-http-persistent", "~> 2.9"
37
49
  spec.add_runtime_dependency "redis", ">= 3.2"
38
- spec.add_runtime_dependency "digest-murmurhash", ">= 1.1"
39
50
  end
@@ -1,4 +1,4 @@
1
1
  desc "Open an irb session preloaded with this library"
2
- task :console do
2
+ task :irb do
3
3
  sh "irb -rubygems -I lib -r splitclient-rb.rb"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splitclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.4.0
4
+ version: 4.5.1.pre.dev
5
5
  platform: ruby
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-23 00:00:00.000000000 Z
11
+ date: 2018-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -39,21 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: webmock
42
+ name: rake-compiler
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - ">="
@@ -67,21 +53,7 @@ dependencies:
67
53
  - !ruby/object:Gem::Version
68
54
  version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: byebug
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: pry
56
+ name: rspec
85
57
  requirement: !ruby/object:Gem::Requirement
86
58
  requirements:
87
59
  - - ">="
@@ -95,7 +67,7 @@ dependencies:
95
67
  - !ruby/object:Gem::Version
96
68
  version: '0'
97
69
  - !ruby/object:Gem::Dependency
98
- name: pry-byebug
70
+ name: webmock
99
71
  requirement: !ruby/object:Gem::Requirement
100
72
  requirements:
101
73
  - - ">="
@@ -220,26 +192,13 @@ dependencies:
220
192
  - - ">="
221
193
  - !ruby/object:Gem::Version
222
194
  version: '3.2'
223
- - !ruby/object:Gem::Dependency
224
- name: digest-murmurhash
225
- requirement: !ruby/object:Gem::Requirement
226
- requirements:
227
- - - ">="
228
- - !ruby/object:Gem::Version
229
- version: '1.1'
230
- type: :runtime
231
- prerelease: false
232
- version_requirements: !ruby/object:Gem::Requirement
233
- requirements:
234
- - - ">="
235
- - !ruby/object:Gem::Version
236
- version: '1.1'
237
195
  description: Ruby client for using split SDK.
238
196
  email:
239
197
  - pato@split.io
240
198
  executables:
241
199
  - splitio
242
- extensions: []
200
+ extensions:
201
+ - ext/murmurhash/extconf.rb
243
202
  extra_rdoc_files: []
244
203
  files:
245
204
  - ".gitignore"
@@ -250,8 +209,14 @@ files:
250
209
  - NEWS
251
210
  - README.md
252
211
  - Rakefile
253
- - console
254
212
  - exe/splitio
213
+ - ext/murmurhash/3_x86_32.c
214
+ - ext/murmurhash/extconf.rb
215
+ - ext/murmurhash/murmurhash.c
216
+ - ext/murmurhash/murmurhash.h
217
+ - lib/murmurhash/base.rb
218
+ - lib/murmurhash/murmurhash.jar
219
+ - lib/murmurhash/murmurhash_mri.rb
255
220
  - lib/splitclient-rb.rb
256
221
  - lib/splitclient-rb/cache/adapters/memory_adapter.rb
257
222
  - lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb
@@ -328,12 +293,10 @@ files:
328
293
  - lib/splitclient-rb/split_factory_builder.rb
329
294
  - lib/splitclient-rb/utilitites.rb
330
295
  - lib/splitclient-rb/version.rb
331
- - runTests
332
296
  - splitclient-rb.gemspec
333
297
  - splitio.yml.example
334
298
  - tasks/benchmark_get_treatment.rake
335
- - tasks/benchmark_hashing_algorithm.rake
336
- - tasks/console.rake
299
+ - tasks/irb.rake
337
300
  - tasks/rspec.rake
338
301
  homepage: https://github.com/splitio/ruby-client
339
302
  licenses:
@@ -350,12 +313,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
350
313
  version: '0'
351
314
  required_rubygems_version: !ruby/object:Gem::Requirement
352
315
  requirements:
353
- - - ">="
316
+ - - ">"
354
317
  - !ruby/object:Gem::Version
355
- version: '0'
318
+ version: 1.3.1
356
319
  requirements: []
357
320
  rubyforge_project:
358
- rubygems_version: 2.5.1
321
+ rubygems_version: 2.6.8
359
322
  signing_key:
360
323
  specification_version: 4
361
324
  summary: Ruby client for split SDK.
data/console DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/bash
2
- bundle exec rake console
data/runTests DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/bash
2
- SPLITCLIENT_ENV=test bundle exec rspec spec
@@ -1,51 +0,0 @@
1
- require 'benchmark'
2
- require 'digest/murmurhash'
3
-
4
- desc 'Benchmark murmur32 hashing algorithm'
5
-
6
- task :benchmark_hashing_algorithm do
7
- iterations = 200_000
8
- key = SecureRandom.uuid
9
-
10
- Benchmark.bmbm do |x|
11
- x.report('MurmurHash1') do
12
- iterations.times { Digest::MurmurHash1.rawdigest(key) }
13
- end
14
-
15
- x.report('MurmurHash2') do
16
- iterations.times { Digest::MurmurHash2.rawdigest(key) }
17
- end
18
-
19
- x.report('MurmurHash2A') do
20
- iterations.times { Digest::MurmurHash2A.rawdigest(key) }
21
- end
22
-
23
- x.report('LegacyHash') do
24
- iterations.times { legacy_hash(key, 123) }
25
- end
26
- end
27
- end
28
-
29
- def legacy_hash(key, seed)
30
- h = 0
31
- for i in 0..key.length-1
32
- h = to_int32(31 * h + key[i].ord)
33
- end
34
- h^seed
35
- end
36
-
37
- def to_int32(number)
38
- begin
39
- sign = number < 0 ? -1 : 1
40
- abs = number.abs
41
- return 0 if abs == 0 || abs == Float::INFINITY
42
- rescue
43
- return 0
44
- end
45
-
46
- pos_int = sign * abs.floor
47
- int_32bit = pos_int % 2**32
48
-
49
- return int_32bit - 2**32 if int_32bit >= 2**31
50
- int_32bit
51
- end