ox 2.14.27 → 2.14.29

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.
data/ext/ox/cache.c CHANGED
@@ -27,6 +27,23 @@
27
27
  // almost the Murmur hash algorithm
28
28
  #define M 0x5bd1e995
29
29
 
30
+ // Mixed into the initial state of hash_calc(). Element and attribute names come
31
+ // straight from the document, so without a per process seed the bucket every
32
+ // name lands in can be precomputed offline. Written once by ox_hash_seed_init()
33
+ // during Init_ox and only read afterwards, so the caches stay Ractor safe.
34
+ static uint64_t hash_seed = 0;
35
+
36
+ void ox_hash_seed_init(void) {
37
+ #ifdef HAVE_RB_HASH_START
38
+ // Folds in the random seed Ruby uses to defend Hash against the same attack.
39
+ hash_seed = (uint64_t)rb_hash_start(0);
40
+ #else
41
+ // String#hash is per process randomized too. It can be negative, so take it
42
+ // as signed and reinterpret rather than letting NUM2ULL reject it.
43
+ hash_seed = (uint64_t)(int64_t)NUM2LL(rb_funcall(rb_str_new_literal("ox"), rb_intern("hash"), 0));
44
+ #endif
45
+ }
46
+
30
47
  typedef struct _slot {
31
48
  struct _slot *next;
32
49
  VALUE val;
@@ -68,7 +85,7 @@ const rb_data_type_t ox_cache_type = {
68
85
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
69
86
  const uint8_t *end = key + len;
70
87
  const uint8_t *endless = key + (len & 0xFFFFFFFC);
71
- uint64_t h = (uint64_t)len;
88
+ uint64_t h = (uint64_t)len ^ hash_seed;
72
89
  uint64_t k;
73
90
 
74
91
  while (key < endless) {
@@ -100,14 +117,19 @@ static uint64_t hash_calc(const uint8_t *key, size_t len) {
100
117
  }
101
118
 
102
119
  static void rehash(Cache c) {
103
- uint64_t osize;
120
+ uint64_t osize = c->size;
121
+ Slot *slots;
104
122
  Slot *end;
105
123
  Slot *sp;
106
124
 
107
- osize = c->size;
125
+ // Growing is only an optimization, and rehash() runs with the lock held, so
126
+ // on failure keep the current size rather than raising.
127
+ if (NULL == (slots = realloc((void *)c->slots, sizeof(Slot) * osize * 4))) {
128
+ return;
129
+ }
108
130
  c->size = osize * 4;
109
131
  c->mask = c->size - 1;
110
- c->slots = realloc((void *)c->slots, sizeof(Slot) * c->size);
132
+ c->slots = slots;
111
133
  memset((Slot *)c->slots + osize, 0, sizeof(Slot) * osize * 3);
112
134
  end = (Slot *)c->slots + osize;
113
135
  for (sp = (Slot *)c->slots; sp < end; sp++) {
@@ -153,7 +175,10 @@ static VALUE ox_lockless_intern(Cache c, const char *key, size_t len, const char
153
175
  }
154
176
  rkey = c->form(key, len);
155
177
  if (NULL == (b = c->reuse)) {
156
- b = calloc(1, sizeof(struct _slot));
178
+ if (NULL == (b = calloc(1, sizeof(struct _slot)))) {
179
+ // Nothing modified yet and no lock held, so raising is safe.
180
+ rb_memerror();
181
+ }
157
182
  } else {
158
183
  c->reuse = b->next;
159
184
  c->rcnt--;
@@ -215,8 +240,8 @@ static VALUE ox_locking_intern(Cache c, const char *key, size_t len, const char
215
240
  c->rcnt--;
216
241
  }
217
242
  CACHE_UNLOCK(c);
218
- if (NULL == b) {
219
- b = calloc(1, sizeof(struct _slot));
243
+ if (NULL == b && NULL == (b = calloc(1, sizeof(struct _slot)))) {
244
+ rb_memerror(); // the lock was released above
220
245
  }
221
246
  rkey = c->form(key, len);
222
247
  b->hash = h;
@@ -250,6 +275,9 @@ Cache ox_cache_create(size_t size, VALUE (*form)(const char *str, size_t len), b
250
275
  Cache c = calloc(1, sizeof(struct _cache));
251
276
  int shift = 0;
252
277
 
278
+ if (NULL == c) {
279
+ rb_memerror();
280
+ }
253
281
  for (; REHASH_LIMIT < size; size /= 2, shift++) {
254
282
  }
255
283
  if (shift < MIN_SHIFT) {
@@ -260,9 +288,12 @@ Cache ox_cache_create(size_t size, VALUE (*form)(const char *str, size_t len), b
260
288
  #else
261
289
  c->mutex = rb_mutex_new();
262
290
  #endif
263
- c->size = 1 << shift;
264
- c->mask = c->size - 1;
265
- c->slots = calloc(c->size, sizeof(Slot));
291
+ c->size = 1 << shift;
292
+ c->mask = c->size - 1;
293
+ if (NULL == (c->slots = calloc(c->size, sizeof(Slot)))) {
294
+ free(c);
295
+ rb_memerror();
296
+ }
266
297
  c->form = form;
267
298
  c->xrate = 1; // low
268
299
  c->mark = mark;
@@ -277,16 +308,20 @@ Cache ox_cache_create(size_t size, VALUE (*form)(const char *str, size_t len), b
277
308
  void ox_cache_free(void *ptr) {
278
309
  Cache c = (Cache)ptr;
279
310
  uint64_t i;
311
+ Slot next;
312
+ Slot s;
280
313
 
281
314
  for (i = 0; i < c->size; i++) {
282
- Slot next;
283
- Slot s;
284
-
285
315
  for (s = c->slots[i]; NULL != s; s = next) {
286
316
  next = s->next;
287
317
  free(s);
288
318
  }
289
319
  }
320
+ // ox_cache_mark retires unused slots onto the reuse list, off the buckets.
321
+ for (s = c->reuse; NULL != s; s = next) {
322
+ next = s->next;
323
+ free(s);
324
+ }
290
325
  free((void *)c->slots);
291
326
  free(c);
292
327
  }
data/ext/ox/cache.h CHANGED
@@ -13,6 +13,8 @@ struct _cache;
13
13
 
14
14
  extern const rb_data_type_t ox_cache_type;
15
15
 
16
+ extern void ox_hash_seed_init(void);
17
+
16
18
  extern struct _cache *ox_cache_create(size_t size, VALUE (*form)(const char *str, size_t len), bool mark, bool locking);
17
19
  extern void ox_cache_free(void *ptr);
18
20
  extern void ox_cache_mark(void *ptr);