opal-up 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +97 -29
  4. data/bin/up_ruby +4 -0
  5. data/bin/up_ruby_cluster +4 -0
  6. data/ext/up_ext/App.h +606 -0
  7. data/ext/up_ext/AsyncSocket.h +355 -0
  8. data/ext/up_ext/AsyncSocketData.h +87 -0
  9. data/ext/up_ext/BloomFilter.h +83 -0
  10. data/ext/up_ext/ChunkedEncoding.h +236 -0
  11. data/ext/up_ext/ClientApp.h +36 -0
  12. data/ext/up_ext/HttpContext.h +502 -0
  13. data/ext/up_ext/HttpContextData.h +56 -0
  14. data/ext/up_ext/HttpErrors.h +53 -0
  15. data/ext/up_ext/HttpParser.h +680 -0
  16. data/ext/up_ext/HttpResponse.h +578 -0
  17. data/ext/up_ext/HttpResponseData.h +95 -0
  18. data/ext/up_ext/HttpRouter.h +380 -0
  19. data/ext/up_ext/Loop.h +204 -0
  20. data/ext/up_ext/LoopData.h +112 -0
  21. data/ext/up_ext/MoveOnlyFunction.h +377 -0
  22. data/ext/up_ext/PerMessageDeflate.h +315 -0
  23. data/ext/up_ext/ProxyParser.h +163 -0
  24. data/ext/up_ext/QueryParser.h +120 -0
  25. data/ext/up_ext/TopicTree.h +363 -0
  26. data/ext/up_ext/Utilities.h +66 -0
  27. data/ext/up_ext/WebSocket.h +381 -0
  28. data/ext/up_ext/WebSocketContext.h +434 -0
  29. data/ext/up_ext/WebSocketContextData.h +109 -0
  30. data/ext/up_ext/WebSocketData.h +86 -0
  31. data/ext/up_ext/WebSocketExtensions.h +256 -0
  32. data/ext/up_ext/WebSocketHandshake.h +145 -0
  33. data/ext/up_ext/WebSocketProtocol.h +506 -0
  34. data/ext/up_ext/bsd.c +767 -0
  35. data/ext/up_ext/bsd.h +109 -0
  36. data/ext/up_ext/context.c +524 -0
  37. data/ext/up_ext/epoll_kqueue.c +458 -0
  38. data/ext/up_ext/epoll_kqueue.h +67 -0
  39. data/ext/up_ext/extconf.rb +5 -0
  40. data/ext/up_ext/internal.h +224 -0
  41. data/ext/up_ext/libusockets.h +350 -0
  42. data/ext/up_ext/libuwebsockets.cpp +1344 -0
  43. data/ext/up_ext/libuwebsockets.h +396 -0
  44. data/ext/up_ext/loop.c +386 -0
  45. data/ext/up_ext/loop_data.h +38 -0
  46. data/ext/up_ext/socket.c +231 -0
  47. data/ext/up_ext/up_ext.c +930 -0
  48. data/lib/up/bun/rack_env.rb +1 -13
  49. data/lib/up/bun/server.rb +93 -19
  50. data/lib/up/cli.rb +3 -0
  51. data/lib/up/client.rb +68 -0
  52. data/lib/up/ruby/cluster.rb +39 -0
  53. data/lib/up/ruby/cluster_cli.rb +10 -0
  54. data/lib/up/{node → ruby}/rack_cluster.rb +5 -4
  55. data/lib/up/{node → ruby}/rack_server.rb +4 -4
  56. data/lib/up/ruby/server_cli.rb +10 -0
  57. data/lib/up/u_web_socket/cluster.rb +18 -3
  58. data/lib/up/u_web_socket/server.rb +108 -15
  59. data/lib/up/version.rb +1 -1
  60. metadata +72 -30
  61. data/.gitignore +0 -5
  62. data/Gemfile +0 -2
  63. data/bin/up_node +0 -12
  64. data/bin/up_node_cluster +0 -12
  65. data/example_rack_app/Gemfile +0 -3
  66. data/example_rack_app/config.ru +0 -6
  67. data/example_rack_app/rack_app.rb +0 -5
  68. data/example_roda_app/Gemfile +0 -6
  69. data/example_roda_app/config.ru +0 -6
  70. data/example_roda_app/roda_app.rb +0 -37
  71. data/example_sinatra_app/Gemfile +0 -6
  72. data/example_sinatra_app/config.ru +0 -6
  73. data/example_sinatra_app/sinatra_app.rb +0 -7
  74. data/lib/up/node/cluster.rb +0 -39
  75. data/lib/up/node/cluster_cli.rb +0 -15
  76. data/lib/up/node/rack_env.rb +0 -106
  77. data/lib/up/node/server.rb +0 -84
  78. data/lib/up/node/server_cli.rb +0 -15
  79. data/lib/up/u_web_socket/rack_env.rb +0 -101
  80. data/opal-up.gemspec +0 -27
  81. data/up_logo.svg +0 -256
@@ -0,0 +1,355 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2020.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef UWS_ASYNCSOCKET_H
19
+ #define UWS_ASYNCSOCKET_H
20
+
21
+ /* This class implements async socket memory management strategies */
22
+
23
+ /* NOTE: Many unsigned/signed conversion warnings could be solved by moving from int length
24
+ * to unsigned length for everything to/from uSockets - this would however remove the opportunity
25
+ * to signal error with -1 (which is how the entire UNIX syscalling is built). */
26
+
27
+ #include <cstring>
28
+ #include <iostream>
29
+
30
+ #include "libusockets.h"
31
+
32
+ #include "LoopData.h"
33
+ #include "AsyncSocketData.h"
34
+
35
+ namespace uWS {
36
+
37
+ enum SendBufferAttribute {
38
+ NEEDS_NOTHING,
39
+ NEEDS_DRAIN,
40
+ NEEDS_UNCORK
41
+ };
42
+
43
+ template <bool, bool, typename> struct WebSocketContext;
44
+
45
+ template <bool SSL>
46
+ struct AsyncSocket {
47
+ /* This guy is promiscuous */
48
+ template <bool> friend struct HttpContext;
49
+ template <bool, bool, typename> friend struct WebSocketContext;
50
+ template <bool> friend struct TemplatedApp;
51
+ template <bool, typename> friend struct WebSocketContextData;
52
+ template <typename, typename> friend struct TopicTree;
53
+ template <bool> friend struct HttpResponse;
54
+
55
+ private:
56
+ /* Helper, do not use directly (todo: move to uSockets or de-crazify) */
57
+ void throttle_helper(int toggle) {
58
+ /* These should be exposed by uSockets */
59
+ static thread_local int us_events[2] = {0, 0};
60
+
61
+ struct us_poll_t *p = (struct us_poll_t *) this;
62
+ struct us_loop_t *loop = us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this));
63
+
64
+ if (toggle) {
65
+ /* Pause */
66
+ int events = us_poll_events(p);
67
+ if (events) {
68
+ us_events[getBufferedAmount() ? 1 : 0] = events;
69
+ }
70
+ us_poll_change(p, loop, 0);
71
+ } else {
72
+ /* Resume */
73
+ int events = us_events[getBufferedAmount() ? 1 : 0];
74
+ us_poll_change(p, loop, events);
75
+ }
76
+ }
77
+
78
+ protected:
79
+ /* Returns SSL pointer or FD as pointer */
80
+ void *getNativeHandle() {
81
+ return us_socket_get_native_handle(SSL, (us_socket_t *) this);
82
+ }
83
+
84
+ /* Get loop data for socket */
85
+ LoopData *getLoopData() {
86
+ return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this)));
87
+ }
88
+
89
+ /* Get socket extension */
90
+ AsyncSocketData<SSL> *getAsyncSocketData() {
91
+ return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this);
92
+ }
93
+
94
+ /* Socket timeout */
95
+ void timeout(unsigned int seconds) {
96
+ us_socket_timeout(SSL, (us_socket_t *) this, seconds);
97
+ }
98
+
99
+ /* Shutdown socket without any automatic drainage */
100
+ void shutdown() {
101
+ us_socket_shutdown(SSL, (us_socket_t *) this);
102
+ }
103
+
104
+ /* Experimental pause */
105
+ us_socket_t *pause() {
106
+ throttle_helper(1);
107
+ return (us_socket_t *) this;
108
+ }
109
+
110
+ /* Experimental resume */
111
+ us_socket_t *resume() {
112
+ throttle_helper(0);
113
+ return (us_socket_t *) this;
114
+ }
115
+
116
+ /* Immediately close socket */
117
+ us_socket_t *close() {
118
+ return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr);
119
+ }
120
+
121
+ void corkUnchecked() {
122
+ /* What if another socket is corked? */
123
+ getLoopData()->corkedSocket = this;
124
+ }
125
+
126
+ void uncorkWithoutSending() {
127
+ if (isCorked()) {
128
+ getLoopData()->corkedSocket = nullptr;
129
+ }
130
+ }
131
+
132
+ /* Cork this socket. Only one socket may ever be corked per-loop at any given time */
133
+ void cork() {
134
+ /* Extra check for invalid corking of others */
135
+ if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) {
136
+ std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl;
137
+ std::terminate();
138
+ }
139
+
140
+ /* What if another socket is corked? */
141
+ getLoopData()->corkedSocket = this;
142
+ }
143
+
144
+ /* Returns wheter we are corked or not */
145
+ bool isCorked() {
146
+ return getLoopData()->corkedSocket == this;
147
+ }
148
+
149
+ /* Returns whether we could cork (it is free) */
150
+ bool canCork() {
151
+ return getLoopData()->corkedSocket == nullptr;
152
+ }
153
+
154
+ /* Returns a suitable buffer for temporary assemblation of send data */
155
+ std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) {
156
+ /* First step is to determine if we already have backpressure or not */
157
+ LoopData *loopData = getLoopData();
158
+ BackPressure &backPressure = getAsyncSocketData()->buffer;
159
+ size_t existingBackpressure = backPressure.length();
160
+ if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) {
161
+ /* Cork automatically if we can */
162
+ if (isCorked()) {
163
+ char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
164
+ loopData->corkOffset += (unsigned int) size;
165
+ return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING};
166
+ } else {
167
+ cork();
168
+ char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
169
+ loopData->corkOffset += (unsigned int) size;
170
+ return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK};
171
+ }
172
+ } else {
173
+
174
+ /* If we are corked and there is already data in the cork buffer,
175
+ mark how much is ours and reset it */
176
+ unsigned int ourCorkOffset = 0;
177
+ if (isCorked() && loopData->corkOffset) {
178
+ ourCorkOffset = loopData->corkOffset;
179
+ loopData->corkOffset = 0;
180
+ }
181
+
182
+ /* Fallback is to use the backpressure as buffer */
183
+ backPressure.resize(ourCorkOffset + existingBackpressure + size);
184
+
185
+ /* And copy corkbuffer in front */
186
+ memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset);
187
+
188
+ return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN};
189
+ }
190
+ }
191
+
192
+ /* Returns the user space backpressure. */
193
+ unsigned int getBufferedAmount() {
194
+ /* We return the actual amount of bytes in backbuffer, including pendingRemoval */
195
+ return (unsigned int) getAsyncSocketData()->buffer.totalLength();
196
+ }
197
+
198
+ /* Returns the text representation of an IPv4 or IPv6 address */
199
+ std::string_view addressAsText(std::string_view binary) {
200
+ static thread_local char buf[64];
201
+ int ipLength = 0;
202
+
203
+ if (!binary.length()) {
204
+ return {};
205
+ }
206
+
207
+ unsigned char *b = (unsigned char *) binary.data();
208
+
209
+ if (binary.length() == 4) {
210
+ ipLength = snprintf(buf, 64, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]);
211
+ } else {
212
+ ipLength = snprintf(buf, 64, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
213
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11],
214
+ b[12], b[13], b[14], b[15]);
215
+ }
216
+
217
+ return {buf, (unsigned int) ipLength};
218
+ }
219
+
220
+ /* Returns the remote IP address or empty string on failure */
221
+ std::string_view getRemoteAddress() {
222
+ static thread_local char buf[16];
223
+ int ipLength = 16;
224
+ us_socket_remote_address(SSL, (us_socket_t *) this, buf, &ipLength);
225
+ return std::string_view(buf, (unsigned int) ipLength);
226
+ }
227
+
228
+ /* Returns the text representation of IP */
229
+ std::string_view getRemoteAddressAsText() {
230
+ return addressAsText(getRemoteAddress());
231
+ }
232
+
233
+ /* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible.
234
+ * Returns pair of bytes written (anywhere) and wheter or not this call resulted in the polling for
235
+ * writable (or we are in a state that implies polling for writable). */
236
+ std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) {
237
+ /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */
238
+ if (us_socket_is_closed(SSL, (us_socket_t *) this)) {
239
+ return {length, false};
240
+ }
241
+
242
+ LoopData *loopData = getLoopData();
243
+ AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData();
244
+
245
+ /* We are limited if we have a per-socket buffer */
246
+ if (asyncSocketData->buffer.length()) {
247
+ /* Write off as much as we can */
248
+ int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length);
249
+
250
+ /* On failure return, otherwise continue down the function */
251
+ if ((unsigned int) written < asyncSocketData->buffer.length()) {
252
+
253
+ /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */
254
+ asyncSocketData->buffer.erase((unsigned int) written);
255
+
256
+ if (optionally) {
257
+ /* Thankfully we can exit early here */
258
+ return {0, true};
259
+ } else {
260
+ /* This path is horrible and points towards erroneous usage */
261
+ asyncSocketData->buffer.append(src, (unsigned int) length);
262
+
263
+ return {length, true};
264
+ }
265
+ }
266
+
267
+ /* At this point we simply have no buffer and can continue as normal */
268
+ asyncSocketData->buffer.clear();
269
+ }
270
+
271
+ if (length) {
272
+ if (loopData->corkedSocket == this) {
273
+ /* We are corked */
274
+ if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) {
275
+ /* If the entire chunk fits in cork buffer */
276
+ memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length);
277
+ loopData->corkOffset += (unsigned int) length;
278
+ /* Fall through to default return */
279
+ } else {
280
+ /* Strategy differences between SSL and non-SSL regarding syscall minimizing */
281
+ if constexpr (false) {
282
+ /* Cork up as much as we can */
283
+ unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset;
284
+ memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped);
285
+ loopData->corkOffset = LoopData::CORK_BUFFER_SIZE;
286
+
287
+ auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally);
288
+ return {written + (int) stripped, failed};
289
+ }
290
+
291
+ /* For non-SSL we take the penalty of two syscalls */
292
+ return uncork(src, length, optionally);
293
+ }
294
+ } else {
295
+ /* We are not corked */
296
+ int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0);
297
+
298
+ /* Did we fail? */
299
+ if (written < length) {
300
+ /* If the write was optional then just bail out */
301
+ if (optionally) {
302
+ return {written, true};
303
+ }
304
+
305
+ /* Fall back to worst possible case (should be very rare for HTTP) */
306
+ /* At least we can reserve room for next chunk if we know it up front */
307
+ if (nextLength) {
308
+ asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength));
309
+ }
310
+
311
+ /* Buffer this chunk */
312
+ asyncSocketData->buffer.append(src + written, (size_t) (length - written));
313
+
314
+ /* Return the failure */
315
+ return {length, true};
316
+ }
317
+ /* Fall through to default return */
318
+ }
319
+ }
320
+
321
+ /* Default fall through return */
322
+ return {length, false};
323
+ }
324
+
325
+ /* Uncork this socket and flush or buffer any corked and/or passed data. It is essential to remember doing this. */
326
+ /* It does NOT count bytes written from cork buffer (they are already accounted for in the write call responsible for its corking)! */
327
+ std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) {
328
+ LoopData *loopData = getLoopData();
329
+
330
+ if (loopData->corkedSocket == this) {
331
+ loopData->corkedSocket = nullptr;
332
+
333
+ if (loopData->corkOffset) {
334
+ /* Corked data is already accounted for via its write call */
335
+ auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length);
336
+ loopData->corkOffset = 0;
337
+
338
+ if (failed) {
339
+ /* We do not need to care for buffering here, write does that */
340
+ return {0, true};
341
+ }
342
+ }
343
+
344
+ /* We should only return with new writes, not things written to cork already */
345
+ return write(src, length, optionally, 0);
346
+ } else {
347
+ /* We are not even corked! */
348
+ return {0, false};
349
+ }
350
+ }
351
+ };
352
+
353
+ }
354
+
355
+ #endif // UWS_ASYNCSOCKET_H
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2021.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef UWS_ASYNCSOCKETDATA_H
19
+ #define UWS_ASYNCSOCKETDATA_H
20
+
21
+ #include <string>
22
+
23
+ namespace uWS {
24
+
25
+ struct BackPressure {
26
+ std::string buffer;
27
+ unsigned int pendingRemoval = 0;
28
+ BackPressure(BackPressure &&other) {
29
+ buffer = std::move(other.buffer);
30
+ pendingRemoval = other.pendingRemoval;
31
+ }
32
+ BackPressure() = default;
33
+ void append(const char *data, size_t length) {
34
+ buffer.append(data, length);
35
+ }
36
+ void erase(unsigned int length) {
37
+ pendingRemoval += length;
38
+ /* Always erase a minimum of 1/32th the current backpressure */
39
+ if (pendingRemoval > (buffer.length() >> 5)) {
40
+ buffer.erase(0, pendingRemoval);
41
+ pendingRemoval = 0;
42
+ }
43
+ }
44
+ size_t length() {
45
+ return buffer.length() - pendingRemoval;
46
+ }
47
+ void clear() {
48
+ pendingRemoval = 0;
49
+ buffer.clear();
50
+ }
51
+ void reserve(size_t length) {
52
+ buffer.reserve(length + pendingRemoval);
53
+ }
54
+ void resize(size_t length) {
55
+ buffer.resize(length + pendingRemoval);
56
+ }
57
+ const char *data() {
58
+ return buffer.data() + pendingRemoval;
59
+ }
60
+ size_t size() {
61
+ return length();
62
+ }
63
+ /* The total length, incuding pending removal */
64
+ size_t totalLength() {
65
+ return buffer.length();
66
+ }
67
+ };
68
+
69
+ /* Depending on how we want AsyncSocket to function, this will need to change */
70
+
71
+ template <bool SSL>
72
+ struct AsyncSocketData {
73
+ /* This will do for now */
74
+ BackPressure buffer;
75
+
76
+ /* Allow move constructing us */
77
+ AsyncSocketData(BackPressure &&backpressure) : buffer(std::move(backpressure)) {
78
+
79
+ }
80
+
81
+ /* Or emppty */
82
+ AsyncSocketData() = default;
83
+ };
84
+
85
+ }
86
+
87
+ #endif // UWS_ASYNCSOCKETDATA_H
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2022.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef UWS_BLOOMFILTER_H
19
+ #define UWS_BLOOMFILTER_H
20
+
21
+ /* This filter has no false positives or collisions for the standard
22
+ * and non-standard common request headers */
23
+
24
+ #include <cstdint>
25
+ #include <string_view>
26
+ #include <bitset>
27
+
28
+ namespace uWS {
29
+
30
+ struct BloomFilter {
31
+ private:
32
+ std::bitset<256> filter;
33
+ static inline uint32_t perfectHash(uint32_t features) {
34
+ return features *= 1843993368;
35
+ }
36
+
37
+ union ScrambleArea {
38
+ unsigned char p[4];
39
+ uint32_t val;
40
+ };
41
+
42
+ ScrambleArea getFeatures(std::string_view key) {
43
+ ScrambleArea s;
44
+ s.p[0] = reinterpret_cast<const unsigned char&>(key[0]);
45
+ s.p[1] = reinterpret_cast<const unsigned char&>(key[key.length() - 1]);
46
+ s.p[2] = reinterpret_cast<const unsigned char&>(key[key.length() - 2]);
47
+ s.p[3] = reinterpret_cast<const unsigned char&>(key[key.length() >> 1]);
48
+ return s;
49
+ }
50
+
51
+ public:
52
+ bool mightHave(std::string_view key) {
53
+ if (key.length() < 2) {
54
+ return true;
55
+ }
56
+
57
+ ScrambleArea s = getFeatures(key);
58
+ s.val = perfectHash(s.val);
59
+ return filter[s.p[0]] &&
60
+ filter[s.p[1]] &&
61
+ filter[s.p[2]] &&
62
+ filter[s.p[3]];
63
+ }
64
+
65
+ void add(std::string_view key) {
66
+ if (key.length() >= 2) {
67
+ ScrambleArea s = getFeatures(key);
68
+ s.val = perfectHash(s.val);
69
+ filter[s.p[0]] = 1;
70
+ filter[s.p[1]] = 1;
71
+ filter[s.p[2]] = 1;
72
+ filter[s.p[3]] = 1;
73
+ }
74
+ }
75
+
76
+ void reset() {
77
+ filter.reset();
78
+ }
79
+ };
80
+
81
+ }
82
+
83
+ #endif // UWS_BLOOMFILTER_H