splitclient-rb 1.0.2.wip → 8.11.1.pre.rc1-java
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 +5 -5
- data/.github/CODEOWNERS +1 -0
- data/.github/pull_request_template.md +9 -0
- data/.github/workflows/ci.yml +90 -0
- data/.github/workflows/update-license-year.yml +45 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +76 -0
- data/.simplecov +1 -0
- data/CHANGES.txt +361 -2
- data/CONTRIBUTORS-GUIDE.md +49 -0
- data/Gemfile +2 -0
- data/LICENSE +3 -36
- data/NOTICE.txt +5 -0
- data/README.md +59 -127
- data/Rakefile +27 -3
- data/ext/murmurhash/MurmurHash3.java +302 -0
- data/gemfiles/faraday_after_0.13.gemfile +7 -0
- data/gemfiles/faraday_before_0.13.gemfile +8 -0
- data/lib/murmurhash/base.rb +58 -0
- data/lib/murmurhash/murmurhash.jar +0 -0
- data/lib/murmurhash/murmurhash_mri.rb +3 -0
- data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +130 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +137 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +53 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +178 -0
- data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
- data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
- data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
- data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
- data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
- data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
- data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
- data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
- data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +45 -0
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +35 -0
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +61 -0
- data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
- data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
- data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +56 -0
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +57 -0
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +23 -0
- data/lib/splitclient-rb/cache/repositories/repository.rb +24 -0
- data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +124 -0
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +296 -0
- data/lib/splitclient-rb/cache/routers/impression_router.rb +60 -0
- data/lib/splitclient-rb/cache/senders/events_sender.rb +42 -0
- data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
- data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
- data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
- data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +95 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +52 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
- data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
- data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
- data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
- data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
- data/lib/splitclient-rb/clients/split_client.rb +530 -0
- data/lib/splitclient-rb/constants.rb +16 -0
- data/lib/splitclient-rb/engine/api/client.rb +87 -0
- data/lib/splitclient-rb/engine/api/events.rb +57 -0
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +58 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +79 -0
- data/lib/splitclient-rb/engine/api/segments.rb +78 -0
- data/lib/splitclient-rb/engine/api/splits.rb +139 -0
- data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
- data/lib/splitclient-rb/engine/auth_api_client.rb +100 -0
- data/lib/splitclient-rb/engine/back_off.rb +26 -0
- data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
- data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
- data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/evaluator/splitter.rb +35 -22
- data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
- data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
- data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
- data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
- data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
- data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
- data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
- data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +10 -21
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +47 -0
- data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/combiners.rb +4 -6
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +80 -0
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +16 -0
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +42 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +37 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +44 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +16 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/matcher.rb +52 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +29 -0
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +47 -0
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +22 -0
- data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
- data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
- data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
- data/lib/splitclient-rb/engine/matchers/set_matcher.rb +27 -0
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +40 -0
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +66 -0
- data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
- data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
- data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
- data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
- data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
- data/lib/splitclient-rb/engine/models/label.rb +10 -0
- data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
- data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
- data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +16 -0
- data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/split.rb +17 -0
- data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
- data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
- data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +271 -0
- data/lib/splitclient-rb/engine/parser/evaluator.rb +112 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/parser/partition.rb +2 -4
- data/lib/splitclient-rb/engine/push_manager.rb +66 -0
- data/lib/splitclient-rb/engine/status_manager.rb +39 -0
- data/lib/splitclient-rb/engine/sync_manager.rb +183 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
- data/lib/splitclient-rb/exceptions.rb +26 -0
- data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
- data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
- data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
- data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
- data/lib/splitclient-rb/helpers/util.rb +26 -0
- data/lib/splitclient-rb/managers/split_manager.rb +121 -0
- data/lib/splitclient-rb/spec.rb +9 -0
- data/lib/splitclient-rb/split_config.rb +591 -47
- data/lib/splitclient-rb/split_factory.rb +284 -0
- data/lib/splitclient-rb/split_factory_builder.rb +7 -0
- data/lib/splitclient-rb/split_factory_registry.rb +63 -0
- data/lib/splitclient-rb/split_logger.rb +23 -0
- data/lib/splitclient-rb/sse/event_source/client.rb +264 -0
- data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
- data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
- data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
- data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
- data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
- data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
- data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
- data/lib/splitclient-rb/sse/workers/splits_worker.rb +153 -0
- data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
- data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
- data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
- data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
- data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
- data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
- data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
- data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
- data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
- data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
- data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
- data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
- data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
- data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
- data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
- data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
- data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
- data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
- data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
- data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
- data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
- data/lib/splitclient-rb/utilitites.rb +49 -0
- data/lib/splitclient-rb/validators.rb +361 -0
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +177 -25
- data/sonar-project.properties +6 -0
- data/splitclient-rb.gemspec +53 -26
- data/tasks/benchmark_get_treatment.rake +31 -17
- data/tasks/irb.rake +6 -0
- metadata +411 -86
- data/NEWS +0 -9
- data/lib/splitclient-cache/local_store.rb +0 -45
- data/lib/splitclient-engine/impressions/impressions.rb +0 -79
- data/lib/splitclient-engine/matchers/between_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/combining_matcher.rb +0 -94
- data/lib/splitclient-engine/matchers/equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/greater_than_or_equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/less_than_or_equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/negation_matcher.rb +0 -54
- data/lib/splitclient-engine/matchers/user_defined_segment_matcher.rb +0 -61
- data/lib/splitclient-engine/matchers/whitelist_matcher.rb +0 -64
- data/lib/splitclient-engine/metrics/binary_search_latency_tracker.rb +0 -122
- data/lib/splitclient-engine/metrics/metrics.rb +0 -158
- data/lib/splitclient-engine/parser/condition.rb +0 -143
- data/lib/splitclient-engine/parser/segment.rb +0 -84
- data/lib/splitclient-engine/parser/segment_parser.rb +0 -46
- data/lib/splitclient-engine/parser/split.rb +0 -68
- data/lib/splitclient-engine/parser/split_adapter.rb +0 -433
- data/lib/splitclient-engine/parser/split_parser.rb +0 -139
- data/lib/splitclient-engine/partitions/treatments.rb +0 -40
- data/lib/splitclient-rb/split_client.rb +0 -156
- data/lib/splitclient-rb_utilitites.rb +0 -26
- data/tasks/console.rake +0 -4
- data/tasks/rspec.rake +0 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The MurmurHash3 algorithm was created by Austin Appleby and placed in the public domain.
|
|
3
|
+
* This java port was authored by Yonik Seeley and also placed into the public domain.
|
|
4
|
+
* The author hereby disclaims copyright to this source code.
|
|
5
|
+
* <p>
|
|
6
|
+
* This produces exactly the same hash values as the final C++
|
|
7
|
+
* version of MurmurHash3 and is thus suitable for producing the same hash values across
|
|
8
|
+
* platforms.
|
|
9
|
+
* <p>
|
|
10
|
+
* The 32 bit x86 version of this hash should be the fastest variant for relatively short keys like ids.
|
|
11
|
+
* murmurhash3_x64_128 is a good choice for longer strings or if you need more than 32 bits of hash.
|
|
12
|
+
* <p>
|
|
13
|
+
* Note - The x86 and x64 versions do _not_ produce the same results, as the
|
|
14
|
+
* algorithms are optimized for their respective platforms.
|
|
15
|
+
* <p>
|
|
16
|
+
* See http://github.com/yonik/java_util for future updates to this file.
|
|
17
|
+
*/
|
|
18
|
+
public final class MurmurHash3 {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 128 bits of state
|
|
22
|
+
*/
|
|
23
|
+
public static final class LongPair {
|
|
24
|
+
public long val1;
|
|
25
|
+
public long val2;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static final int fmix32(int h) {
|
|
29
|
+
h ^= h >>> 16;
|
|
30
|
+
h *= 0x85ebca6b;
|
|
31
|
+
h ^= h >>> 13;
|
|
32
|
+
h *= 0xc2b2ae35;
|
|
33
|
+
h ^= h >>> 16;
|
|
34
|
+
return h;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public static final long fmix64(long k) {
|
|
38
|
+
k ^= k >>> 33;
|
|
39
|
+
k *= 0xff51afd7ed558ccdL;
|
|
40
|
+
k ^= k >>> 33;
|
|
41
|
+
k *= 0xc4ceb9fe1a85ec53L;
|
|
42
|
+
k ^= k >>> 33;
|
|
43
|
+
return k;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets a long from a byte buffer in little endian byte order.
|
|
48
|
+
*/
|
|
49
|
+
public static final long getLongLittleEndian(byte[] buf, int offset) {
|
|
50
|
+
return ((long) buf[offset + 7] << 56) // no mask needed
|
|
51
|
+
| ((buf[offset + 6] & 0xffL) << 48)
|
|
52
|
+
| ((buf[offset + 5] & 0xffL) << 40)
|
|
53
|
+
| ((buf[offset + 4] & 0xffL) << 32)
|
|
54
|
+
| ((buf[offset + 3] & 0xffL) << 24)
|
|
55
|
+
| ((buf[offset + 2] & 0xffL) << 16)
|
|
56
|
+
| ((buf[offset + 1] & 0xffL) << 8)
|
|
57
|
+
| ((buf[offset] & 0xffL)); // no shift needed
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns the MurmurHash3_x86_32 hash of the UTF-8 bytes of the String without actually encoding
|
|
63
|
+
* the string to a temporary buffer. This is more than 2x faster than hashing the result
|
|
64
|
+
* of String.getBytes().
|
|
65
|
+
*/
|
|
66
|
+
public static long murmurhash3_x86_32(CharSequence data, int seed) {
|
|
67
|
+
final int c1 = 0xcc9e2d51;
|
|
68
|
+
final int c2 = 0x1b873593;
|
|
69
|
+
|
|
70
|
+
int h1 = seed;
|
|
71
|
+
|
|
72
|
+
int offset = 0;
|
|
73
|
+
int len = data.length();
|
|
74
|
+
int pos = offset;
|
|
75
|
+
int end = offset + len;
|
|
76
|
+
int k1 = 0;
|
|
77
|
+
int k2 = 0;
|
|
78
|
+
int shift = 0;
|
|
79
|
+
int bits = 0;
|
|
80
|
+
int nBytes = 0; // length in UTF8 bytes
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
while (pos < end) {
|
|
84
|
+
int code = data.charAt(pos++);
|
|
85
|
+
if (code < 0x80) {
|
|
86
|
+
k2 = code;
|
|
87
|
+
bits = 8;
|
|
88
|
+
|
|
89
|
+
} else if (code < 0x800) {
|
|
90
|
+
k2 = (0xC0 | (code >> 6))
|
|
91
|
+
| ((0x80 | (code & 0x3F)) << 8);
|
|
92
|
+
bits = 16;
|
|
93
|
+
} else if (code < 0xD800 || code > 0xDFFF || pos >= end) {
|
|
94
|
+
// we check for pos>=end to encode an unpaired surrogate as 3 bytes.
|
|
95
|
+
k2 = (0xE0 | (code >> 12))
|
|
96
|
+
| ((0x80 | ((code >> 6) & 0x3F)) << 8)
|
|
97
|
+
| ((0x80 | (code & 0x3F)) << 16);
|
|
98
|
+
bits = 24;
|
|
99
|
+
} else {
|
|
100
|
+
// surrogate pair
|
|
101
|
+
// int utf32 = pos < end ? (int) data.charAt(pos++) : 0;
|
|
102
|
+
int utf32 = (int) data.charAt(pos++);
|
|
103
|
+
utf32 = ((code - 0xD7C0) << 10) + (utf32 & 0x3FF);
|
|
104
|
+
k2 = (0xff & (0xF0 | (utf32 >> 18)))
|
|
105
|
+
| ((0x80 | ((utf32 >> 12) & 0x3F))) << 8
|
|
106
|
+
| ((0x80 | ((utf32 >> 6) & 0x3F))) << 16
|
|
107
|
+
| (0x80 | (utf32 & 0x3F)) << 24;
|
|
108
|
+
bits = 32;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
k1 |= k2 << shift;
|
|
113
|
+
|
|
114
|
+
// int used_bits = 32 - shift; // how many bits of k2 were used in k1.
|
|
115
|
+
// int unused_bits = bits - used_bits; // (bits-(32-shift)) == bits+shift-32 == bits-newshift
|
|
116
|
+
|
|
117
|
+
shift += bits;
|
|
118
|
+
if (shift >= 32) {
|
|
119
|
+
// mix after we have a complete word
|
|
120
|
+
|
|
121
|
+
k1 *= c1;
|
|
122
|
+
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
|
|
123
|
+
k1 *= c2;
|
|
124
|
+
|
|
125
|
+
h1 ^= k1;
|
|
126
|
+
h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13);
|
|
127
|
+
h1 = h1 * 5 + 0xe6546b64;
|
|
128
|
+
|
|
129
|
+
shift -= 32;
|
|
130
|
+
// unfortunately, java won't let you shift 32 bits off, so we need to check for 0
|
|
131
|
+
if (shift != 0) {
|
|
132
|
+
k1 = k2 >>> (bits - shift); // bits used == bits - newshift
|
|
133
|
+
} else {
|
|
134
|
+
k1 = 0;
|
|
135
|
+
}
|
|
136
|
+
nBytes += 4;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
} // inner
|
|
140
|
+
|
|
141
|
+
// handle tail
|
|
142
|
+
if (shift > 0) {
|
|
143
|
+
nBytes += shift >> 3;
|
|
144
|
+
k1 *= c1;
|
|
145
|
+
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
|
|
146
|
+
k1 *= c2;
|
|
147
|
+
h1 ^= k1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// finalization
|
|
151
|
+
h1 ^= nBytes;
|
|
152
|
+
|
|
153
|
+
// fmix(h1);
|
|
154
|
+
h1 ^= h1 >>> 16;
|
|
155
|
+
h1 *= 0x85ebca6b;
|
|
156
|
+
h1 ^= h1 >>> 13;
|
|
157
|
+
h1 *= 0xc2b2ae35;
|
|
158
|
+
h1 ^= h1 >>> 16;
|
|
159
|
+
|
|
160
|
+
return h1 & 0xFFFFFFFFL;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// The following set of methods and constants are borrowed from:
|
|
164
|
+
// `This method is borrowed from `org.apache.commons.codec.digest.MurmurHash3`
|
|
165
|
+
|
|
166
|
+
// Constants for 128-bit variant
|
|
167
|
+
private static final long C1 = 0x87c37b91114253d5L;
|
|
168
|
+
private static final long C2 = 0x4cf5ad432745937fL;
|
|
169
|
+
private static final int R1 = 31;
|
|
170
|
+
private static final int R2 = 27;
|
|
171
|
+
private static final int R3 = 33;
|
|
172
|
+
private static final int M = 5;
|
|
173
|
+
private static final int N1 = 0x52dce729;
|
|
174
|
+
private static final int N2 = 0x38495ab5;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Gets the little-endian long from 8 bytes starting at the specified index.
|
|
178
|
+
*
|
|
179
|
+
* @param data The data
|
|
180
|
+
* @param index The index
|
|
181
|
+
* @return The little-endian long
|
|
182
|
+
*/
|
|
183
|
+
private static long getLittleEndianLong(final byte[] data, final int index) {
|
|
184
|
+
return (((long) data[index ] & 0xff) ) |
|
|
185
|
+
(((long) data[index + 1] & 0xff) << 8) |
|
|
186
|
+
(((long) data[index + 2] & 0xff) << 16) |
|
|
187
|
+
(((long) data[index + 3] & 0xff) << 24) |
|
|
188
|
+
(((long) data[index + 4] & 0xff) << 32) |
|
|
189
|
+
(((long) data[index + 5] & 0xff) << 40) |
|
|
190
|
+
(((long) data[index + 6] & 0xff) << 48) |
|
|
191
|
+
(((long) data[index + 7] & 0xff) << 56);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public static long[] hash128x64(final String data, final long seed) {
|
|
195
|
+
final byte[] dataBytes = data.getBytes();
|
|
196
|
+
return hash128x64(dataBytes, 0, dataBytes.length, seed);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generates 128-bit hash from the byte array with the given offset, length and seed.
|
|
201
|
+
*
|
|
202
|
+
* <p>This is an implementation of the 128-bit hash function {@code MurmurHash3_x64_128}
|
|
203
|
+
* from from Austin Applyby's original MurmurHash3 {@code c++} code in SMHasher.</p>
|
|
204
|
+
*
|
|
205
|
+
* @param data The input byte array
|
|
206
|
+
* @param offset The first element of array
|
|
207
|
+
* @param length The length of array
|
|
208
|
+
* @param seed The initial seed value
|
|
209
|
+
* @return The 128-bit hash (2 longs)
|
|
210
|
+
*/
|
|
211
|
+
public static long[] hash128x64(final byte[] data, final int offset, final int length, final long seed) {
|
|
212
|
+
long h1 = seed;
|
|
213
|
+
long h2 = seed;
|
|
214
|
+
final int nblocks = length >> 4;
|
|
215
|
+
|
|
216
|
+
// body
|
|
217
|
+
for (int i = 0; i < nblocks; i++) {
|
|
218
|
+
final int index = offset + (i << 4);
|
|
219
|
+
long k1 = getLittleEndianLong(data, index);
|
|
220
|
+
long k2 = getLittleEndianLong(data, index + 8);
|
|
221
|
+
|
|
222
|
+
// mix functions for k1
|
|
223
|
+
k1 *= C1;
|
|
224
|
+
k1 = Long.rotateLeft(k1, R1);
|
|
225
|
+
k1 *= C2;
|
|
226
|
+
h1 ^= k1;
|
|
227
|
+
h1 = Long.rotateLeft(h1, R2);
|
|
228
|
+
h1 += h2;
|
|
229
|
+
h1 = h1 * M + N1;
|
|
230
|
+
|
|
231
|
+
// mix functions for k2
|
|
232
|
+
k2 *= C2;
|
|
233
|
+
k2 = Long.rotateLeft(k2, R3);
|
|
234
|
+
k2 *= C1;
|
|
235
|
+
h2 ^= k2;
|
|
236
|
+
h2 = Long.rotateLeft(h2, R1);
|
|
237
|
+
h2 += h1;
|
|
238
|
+
h2 = h2 * M + N2;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// tail
|
|
242
|
+
long k1 = 0;
|
|
243
|
+
long k2 = 0;
|
|
244
|
+
final int index = offset + (nblocks << 4);
|
|
245
|
+
switch (offset + length - index) {
|
|
246
|
+
case 15:
|
|
247
|
+
k2 ^= ((long) data[index + 14] & 0xff) << 48;
|
|
248
|
+
case 14:
|
|
249
|
+
k2 ^= ((long) data[index + 13] & 0xff) << 40;
|
|
250
|
+
case 13:
|
|
251
|
+
k2 ^= ((long) data[index + 12] & 0xff) << 32;
|
|
252
|
+
case 12:
|
|
253
|
+
k2 ^= ((long) data[index + 11] & 0xff) << 24;
|
|
254
|
+
case 11:
|
|
255
|
+
k2 ^= ((long) data[index + 10] & 0xff) << 16;
|
|
256
|
+
case 10:
|
|
257
|
+
k2 ^= ((long) data[index + 9] & 0xff) << 8;
|
|
258
|
+
case 9:
|
|
259
|
+
k2 ^= data[index + 8] & 0xff;
|
|
260
|
+
k2 *= C2;
|
|
261
|
+
k2 = Long.rotateLeft(k2, R3);
|
|
262
|
+
k2 *= C1;
|
|
263
|
+
h2 ^= k2;
|
|
264
|
+
|
|
265
|
+
case 8:
|
|
266
|
+
k1 ^= ((long) data[index + 7] & 0xff) << 56;
|
|
267
|
+
case 7:
|
|
268
|
+
k1 ^= ((long) data[index + 6] & 0xff) << 48;
|
|
269
|
+
case 6:
|
|
270
|
+
k1 ^= ((long) data[index + 5] & 0xff) << 40;
|
|
271
|
+
case 5:
|
|
272
|
+
k1 ^= ((long) data[index + 4] & 0xff) << 32;
|
|
273
|
+
case 4:
|
|
274
|
+
k1 ^= ((long) data[index + 3] & 0xff) << 24;
|
|
275
|
+
case 3:
|
|
276
|
+
k1 ^= ((long) data[index + 2] & 0xff) << 16;
|
|
277
|
+
case 2:
|
|
278
|
+
k1 ^= ((long) data[index + 1] & 0xff) << 8;
|
|
279
|
+
case 1:
|
|
280
|
+
k1 ^= data[index] & 0xff;
|
|
281
|
+
k1 *= C1;
|
|
282
|
+
k1 = Long.rotateLeft(k1, R1);
|
|
283
|
+
k1 *= C2;
|
|
284
|
+
h1 ^= k1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// finalization
|
|
288
|
+
h1 ^= length;
|
|
289
|
+
h2 ^= length;
|
|
290
|
+
|
|
291
|
+
h1 += h2;
|
|
292
|
+
h2 += h1;
|
|
293
|
+
|
|
294
|
+
h1 = fmix64(h1);
|
|
295
|
+
h2 = fmix64(h2);
|
|
296
|
+
|
|
297
|
+
h1 += h2;
|
|
298
|
+
h2 += h1;
|
|
299
|
+
|
|
300
|
+
return new long[] { h1, h2 };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -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,130 @@
|
|
|
1
|
+
require 'lru_redux'
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Cache
|
|
5
|
+
module Adapters
|
|
6
|
+
class CacheAdapter
|
|
7
|
+
extend Forwardable
|
|
8
|
+
def_delegators :@adapter, :initialize_set, :set_bool, :pipelined
|
|
9
|
+
|
|
10
|
+
def initialize(config)
|
|
11
|
+
@cache = LruRedux::TTL::ThreadSafeCache.new(config.max_cache_size, config.cache_ttl)
|
|
12
|
+
@adapter = config.cache_adapter
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def delete(key)
|
|
16
|
+
@cache.delete(key)
|
|
17
|
+
@adapter.delete(key)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def clear(namespace_key)
|
|
21
|
+
@cache.clear
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def string(key)
|
|
25
|
+
value = get(key)
|
|
26
|
+
return value if value
|
|
27
|
+
value = @adapter.string(key)
|
|
28
|
+
add(key, value)
|
|
29
|
+
value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def set_string(key, value)
|
|
33
|
+
add(key, value)
|
|
34
|
+
@adapter.set_string(key, value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def multiple_strings(keys)
|
|
38
|
+
cached_values = keys.each_with_object({}) do |key, memo|
|
|
39
|
+
memo[key] = get(key)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
non_cached_keys = []
|
|
43
|
+
cached_values.delete_if{ |k,v| v.nil? ? non_cached_keys << k : false }
|
|
44
|
+
|
|
45
|
+
if non_cached_keys.any?
|
|
46
|
+
new_values = @adapter.multiple_strings(non_cached_keys)
|
|
47
|
+
|
|
48
|
+
new_values.keys.each do |key, value|
|
|
49
|
+
add(key, value)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
cached_values.merge!(new_values)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
cached_values
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def find_strings_by_prefix(prefix)
|
|
59
|
+
@adapter.find_strings_by_prefix(prefix)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def exists?(key)
|
|
63
|
+
cached_value = get(key)
|
|
64
|
+
if cached_value.nil?
|
|
65
|
+
@adapter.exists?(key)
|
|
66
|
+
else
|
|
67
|
+
true
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def add_to_set(key, values)
|
|
72
|
+
if values.is_a? Array
|
|
73
|
+
values.each { |value| add_to_map(key, value, 1) }
|
|
74
|
+
else
|
|
75
|
+
add_to_map(key, values, 1)
|
|
76
|
+
end
|
|
77
|
+
@adapter.add_to_set(key, values)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def in_set?(key, field)
|
|
81
|
+
cached_value = get(key)
|
|
82
|
+
if cached_value.nil?
|
|
83
|
+
return @adapter.in_set?(key, field)
|
|
84
|
+
end
|
|
85
|
+
cached_value.key?(field)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def get_set(key)
|
|
89
|
+
cached_value = get(key)
|
|
90
|
+
if cached_value.nil?
|
|
91
|
+
return @adapter.get_set(key)
|
|
92
|
+
end
|
|
93
|
+
cached_value.keys
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def delete_from_set(key, fields)
|
|
97
|
+
cached_value = get(key)
|
|
98
|
+
if cached_value
|
|
99
|
+
if fields.is_a? Array
|
|
100
|
+
fields.each { |field| cached_value.delete(field) }
|
|
101
|
+
else
|
|
102
|
+
cached_value.delete(fields)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@adapter.delete_from_set(key, fields)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def initialize_map(key)
|
|
110
|
+
@cache[key] = Concurrent::Map.new
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def add_to_map(key, field, value)
|
|
116
|
+
initialize_map(key) unless get(key)
|
|
117
|
+
get(key).put(field.to_s, value.to_s)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def add(key, value)
|
|
121
|
+
@cache[key] = value.to_s unless value.nil?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def get(key)
|
|
125
|
+
@cache[key]
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'concurrent'
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Cache
|
|
5
|
+
module Adapters
|
|
6
|
+
# Memory adapter can have different implementations, this class is used as a delegator to
|
|
7
|
+
# this implementations
|
|
8
|
+
class MemoryAdapter < SimpleDelegator
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require 'concurrent'
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Cache
|
|
5
|
+
module Adapters
|
|
6
|
+
module MemoryAdapters
|
|
7
|
+
# Memory adapter implementation, which stores everything inside thread-safe Map
|
|
8
|
+
class MapAdapter
|
|
9
|
+
def initialize
|
|
10
|
+
@map = Concurrent::Map.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def clear(_ = nil)
|
|
14
|
+
initialize
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Map
|
|
18
|
+
def initialize_map(key)
|
|
19
|
+
@map[key] = Concurrent::Map.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_to_map(key, field, value)
|
|
23
|
+
initialize_map(key) unless @map[key]
|
|
24
|
+
|
|
25
|
+
@map[key].put(field, value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def find_in_map(key, field)
|
|
29
|
+
return nil if @map[key].nil?
|
|
30
|
+
|
|
31
|
+
@map[key].get(field)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def delete_from_map(key, fields)
|
|
35
|
+
if fields.is_a? Array
|
|
36
|
+
fields.each { |field| @map[key].delete(field) }
|
|
37
|
+
else
|
|
38
|
+
@map[key].delete(fields)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def in_map?(key, field)
|
|
43
|
+
return false if @map[key].nil?
|
|
44
|
+
|
|
45
|
+
@map[key].key?(field)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def map_keys(key)
|
|
49
|
+
@map[key].keys
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_map(key)
|
|
53
|
+
@map[key]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# String
|
|
57
|
+
def string(key)
|
|
58
|
+
@map[key]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def set_string(key, str)
|
|
62
|
+
@map[key] = str
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def find_strings_by_prefix(prefix)
|
|
66
|
+
@map.keys.select { |str| str.start_with? prefix }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def multiple_strings(keys)
|
|
70
|
+
keys.each_with_object({}) do |key, memo|
|
|
71
|
+
memo[key] = string(key)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Bool
|
|
76
|
+
def set_bool(key, val)
|
|
77
|
+
@map[key] = val
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def bool(key)
|
|
81
|
+
@map[key]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Set
|
|
85
|
+
alias_method :initialize_set, :initialize_map
|
|
86
|
+
alias_method :get_set, :map_keys
|
|
87
|
+
alias_method :delete_from_set, :delete_from_map
|
|
88
|
+
alias_method :in_set?, :in_map?
|
|
89
|
+
alias_method :find_sets_by_prefix, :find_strings_by_prefix
|
|
90
|
+
|
|
91
|
+
def add_to_set(key, values)
|
|
92
|
+
if values.is_a? Array
|
|
93
|
+
values.each { |value| add_to_map(key, value, 1) }
|
|
94
|
+
else
|
|
95
|
+
add_to_map(key, values, 1)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def get_all_from_set(key)
|
|
100
|
+
@map[key].keys
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def union_sets(set_keys)
|
|
104
|
+
array = set_keys.each_with_object([]) do |key, memo|
|
|
105
|
+
memo << get_set(key)
|
|
106
|
+
end.flatten
|
|
107
|
+
|
|
108
|
+
Set.new(array)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# General
|
|
112
|
+
def exists?(key)
|
|
113
|
+
!@map[key].nil?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def delete(key)
|
|
117
|
+
if key.is_a? Array
|
|
118
|
+
key.each { |k| @map.delete(k) }
|
|
119
|
+
else
|
|
120
|
+
@map.delete(key)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def empty?
|
|
125
|
+
@map.empty?
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# This method is used in Redis adapter
|
|
129
|
+
# "stubbing" it here to keep the interface
|
|
130
|
+
def pipelined(&block)
|
|
131
|
+
block.call
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|