264.patch

Zachary Scott, 04/05/2013 11:43 AM

Download (14.9 KB)

View differences:

load.c
171 171
    rb_ary_replace(vm->loaded_features_snapshot, vm->loaded_features);
172 172
}
173 173

  
174
static struct st_table *
175
get_loaded_features_index_raw(void)
176
{
177
    return GET_VM()->loaded_features_index;
178
}
179

  
180 174
static st_table *
181 175
get_loading_table(void)
182 176
{
183 177
    return GET_VM()->loading_table;
184 178
}
185 179

  
186
static void
187
features_index_add_single(VALUE short_feature, VALUE offset)
180
static inline uint32_t
181
fmix_uint(uint32_t val)
188 182
{
189
    struct st_table *features_index;
190
    VALUE this_feature_index = Qnil;
191
    char *short_feature_cstr;
183
    /* with -O2 even on i386 gcc and clang transform this to
184
     * single multiplication 32bit*32bit=64bit */
185
    uint64_t res = ((uint64_t)val) * (uint64_t)0x85ebca6b;
186
    return (uint32_t)res ^ (uint32_t)(res>>32);
187
}
192 188

  
193
    Check_Type(offset, T_FIXNUM);
194
    Check_Type(short_feature, T_STRING);
195
    short_feature_cstr = StringValueCStr(short_feature);
189
static uint32_t
190
fast_string_hash(const char *str, long len)
191
{
192
    uint32_t res = 0;
193
    for(;len > 0; str+=16, len-=16) {
194
	uint32_t buf[4] = {0, 0, 0, 0};
195
	memcpy(buf, str, len < 16 ? len : 16);
196
	res = fmix_uint(res ^ buf[0]);
197
	res = fmix_uint(res ^ buf[1]);
198
	res = fmix_uint(res ^ buf[2]);
199
	res = fmix_uint(res ^ buf[3]);
200
    }
201
    return res;
202
}
203

  
204
/*
205
 * We build index for loaded features relying on fact that loaded_feature_path
206
 * rechecks feature name. So that, we could store only hash of string instead
207
 * of whole string in a hash.
208
 * Instead of allocation of array of offsets by each feature, we organize
209
 * offsets in a single linked lists - one list per feature - which are stored
210
 * in a single array. And we store in a hash positions of head and tail of
211
 * this list
212
 */
213
#define FI_LAST (-1)
214
#define FI_DEFAULT_HASH_SIZE   (64)
215
#define FI_DEFAULT_LIST_SIZE  (64)
216

  
217
typedef struct features_index_hash_item {
218
    uint32_t hash;
219
    /* we will store position + 1 in head and tail,
220
     * so that if head == 0 then item is free */
221
    int head;
222
    int tail;
223
} fi_hash_item;
224

  
225
typedef struct features_index_hash {
226
    int capa;
227
    int size;
228
    fi_hash_item *items;
229
} fi_hash;
230

  
231
static fi_hash_item *
232
fi_hash_candidate(fi_hash *index, uint32_t hash)
233
{
234
    fi_hash_item *items = index->items;
235
    int capa_1 = index->capa - 1;
236
    int pos = hash & capa_1;
237
    if (items[pos].hash != hash && items[pos].head != 0) {
238
	/* always odd, so that it has no common diviser with capa*/
239
	int step = (hash % capa_1) | 1;
240
	do {
241
	    pos = (pos + step) & capa_1;
242
	} while (items[pos].hash != hash && items[pos].head != 0);
243
    }
244
    return items + pos;
245
}
246

  
247
static void
248
fi_hash_rehash(fi_hash *index)
249
{
250
    fi_hash temp;
251
    int i;
252
    fi_hash_item *items = index->items;
253
    temp.capa = index->capa * 2;
254
    temp.size = index->size;
255
    temp.items = xcalloc(temp.capa, sizeof(fi_hash_item));
256
    for(i=0; i < index->capa; i++) {
257
	if (items[i].head) {
258
	    fi_hash_item *item = fi_hash_candidate(&temp, items[i].hash);
259
	    *item = items[i];
260
	}
261
    }
262
    *index = temp;
263
    xfree(items);
264
}
196 265

  
197
    features_index = get_loaded_features_index_raw();
198
    st_lookup(features_index, (st_data_t)short_feature_cstr, (st_data_t *)&this_feature_index);
266
static int
267
fi_hash_find_head(fi_hash *index, const char *str, long len)
268
{
269
    uint32_t hash = fast_string_hash(str, len);
270
    fi_hash_item *item = fi_hash_candidate(index, hash);
271
    return item->head - 1; /* if head==0 then result is FI_LAST */
272
}
199 273

  
200
    if (NIL_P(this_feature_index)) {
201
	st_insert(features_index, (st_data_t)ruby_strdup(short_feature_cstr), (st_data_t)offset);
274
/* inserts position of tail into hash,
275
 * returns previous tail position or FI_LAST */
276
static int
277
fi_hash_insert_pos(fi_hash *index, const char *str, long len, int pos)
278
{
279
    fi_hash_item *item;
280
    int hash = fast_string_hash(str, len);
281
    if (index->size > index->capa / 4 * 3) {
282
	fi_hash_rehash(index);
202 283
    }
203
    else if (RB_TYPE_P(this_feature_index, T_FIXNUM)) {
204
	VALUE feature_indexes[2];
205
	feature_indexes[0] = this_feature_index;
206
	feature_indexes[1] = offset;
207
	this_feature_index = rb_ary_tmp_new(numberof(feature_indexes));
208
	rb_ary_cat(this_feature_index, feature_indexes, numberof(feature_indexes));
209
	st_insert(features_index, (st_data_t)short_feature_cstr, (st_data_t)this_feature_index);
284
    item = fi_hash_candidate(index, hash);
285
    if (item->head) {
286
	int res = item->tail - 1;
287
	item->tail = pos + 1;
288
	return res;
210 289
    }
211 290
    else {
212
	Check_Type(this_feature_index, T_ARRAY);
213
	rb_ary_push(this_feature_index, offset);
291
	item->hash = hash;
292
	item->head = pos + 1;
293
	item->tail = pos + 1;
294
	index->size++;
295
	return FI_LAST;
296
    }
297
}
298

  
299
typedef struct features_index_multilist_item {
300
    int offset;
301
    int next;
302
} fi_list_item;
303

  
304
typedef struct features_index_multilist {
305
    fi_list_item *items;
306
    int capa;
307
    int size;
308
} fi_list;
309

  
310
static void
311
fi_list_insert_offset(fi_list *list, int offset, int prev_pos)
312
{
313
    if (list->size == list->capa) {
314
	REALLOC_N(list->items, fi_list_item, list->capa*2);
315
	MEMZERO(list->items + list->capa, fi_list_item, list->capa);
316
	list->capa*=2;
214 317
    }
318
    list->items[list->size].offset = offset;
319
    list->items[list->size].next = FI_LAST;
320
    if (prev_pos != FI_LAST) {
321
	list->items[prev_pos].next = list->size;
322
    }
323
    list->size++;
324
}
325

  
326
typedef struct features_index {
327
    fi_hash hash;
328
    fi_list list;
329
} st_features_index;
330

  
331
static void
332
features_index_free(void *p)
333
{
334
    if (p) {
335
	st_features_index *fi = p;
336
	xfree(fi->hash.items);
337
	xfree(fi->list.items);
338
	xfree(fi);
339
    }
340
}
341

  
342
static VALUE
343
features_index_allocate()
344
{
345
    st_features_index *index = xcalloc(1, sizeof(st_features_index));
346
    index->hash.capa = FI_DEFAULT_HASH_SIZE;
347
    index->hash.items = xcalloc(index->hash.capa, sizeof(fi_hash_item));
348
    index->list.capa = FI_DEFAULT_LIST_SIZE;
349
    index->list.items = xcalloc(index->list.capa, sizeof(fi_list_item));
350
    return Data_Wrap_Struct(rb_cObject, 0, features_index_free, index);
351
}
352

  
353
static st_features_index *
354
get_loaded_features_index_raw(void)
355
{
356
    st_features_index *index;
357
    Data_Get_Struct(GET_VM()->loaded_features_index, st_features_index, index);
358
    return index;
359
}
360

  
361
static void
362
features_index_clear()
363
{
364
    st_features_index *index = get_loaded_features_index_raw();
365
    MEMZERO(index->hash.items, fi_hash_item, index->hash.capa);
366
    index->hash.size = 0;
367
    MEMZERO(index->list.items, fi_list_item, index->list.capa);
368
    index->list.size = 0;
369
}
370

  
371
static void
372
features_index_add_single(const char *short_feature, long len, int offset)
373
{
374
    st_features_index *index = get_loaded_features_index_raw();
375
    int prev_pos = fi_hash_insert_pos(&index->hash, short_feature, len, index->list.size);
376
    fi_list_insert_offset(&index->list, offset, prev_pos);
215 377
}
216 378

  
217 379
/* Add to the loaded-features index all the required entries for
......
223 385
   relies on for its fast lookup.
224 386
*/
225 387
static void
226
features_index_add(VALUE feature, VALUE offset)
388
features_index_add(const char *feature_str, long len, int offset)
227 389
{
228
    VALUE short_feature;
229
    const char *feature_str, *feature_end, *ext, *p;
390
    const char *feature_end, *ext, *p;
230 391

  
231
    feature_str = StringValuePtr(feature);
232
    feature_end = feature_str + RSTRING_LEN(feature);
392
    feature_end = feature_str + len;
233 393

  
234 394
    for (ext = feature_end; ext > feature_str; ext--)
235 395
      if (*ext == '.' || *ext == '/')
......
247 407
	if (p < feature_str)
248 408
	    break;
249 409
	/* Now *p == '/'.  We reach this point for every '/' in `feature`. */
250
	short_feature = rb_str_substr(feature, p + 1 - feature_str, feature_end - p - 1);
251
	features_index_add_single(short_feature, offset);
410
	features_index_add_single(p + 1, feature_end - p - 1, offset);
252 411
	if (ext) {
253
	    short_feature = rb_str_substr(feature, p + 1 - feature_str, ext - p - 1);
254
	    features_index_add_single(short_feature, offset);
412
	    features_index_add_single(p + 1, ext - p - 1, offset);
255 413
	}
256 414
    }
257
    features_index_add_single(feature, offset);
415
    features_index_add_single(feature_str, len, offset);
258 416
    if (ext) {
259
	short_feature = rb_str_substr(feature, 0, ext - feature_str);
260
	features_index_add_single(short_feature, offset);
417
	features_index_add_single(feature_str, ext - feature_str, offset);
261 418
    }
262 419
}
263 420

  
264
static int
265
loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
421
static fi_list_item
422
features_index_find(st_features_index *index, const char *feature, long len)
266 423
{
267
    xfree((char *)key);
268
    return ST_DELETE;
424
    int pos = fi_hash_find_head(&index->hash, feature, len);
425
    if (pos == FI_LAST) {
426
	fi_list_item res = {FI_LAST, FI_LAST};
427
	return res;
428
    }
429
    else
430
	return index->list.items[pos];
269 431
}
270 432

  
271
static st_table *
433
static fi_list_item
434
features_index_next(st_features_index *index, fi_list_item cur)
435
{
436
    if (cur.next == FI_LAST) {
437
	fi_list_item res = {FI_LAST, FI_LAST};
438
	return res;
439
    }
440
    else
441
	return index->list.items[cur.next];
442
}
443

  
444
static st_features_index *
272 445
get_loaded_features_index(void)
273 446
{
274 447
    VALUE features;
......
278 451
    if (!rb_ary_shared_with_p(vm->loaded_features_snapshot, vm->loaded_features)) {
279 452
	/* The sharing was broken; something (other than us in rb_provide_feature())
280 453
	   modified loaded_features.  Rebuild the index. */
281
	st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
454
	features_index_clear();
282 455
	features = vm->loaded_features;
283 456
	for (i = 0; i < RARRAY_LEN(features); i++) {
284 457
	    VALUE entry, as_str;
......
287 460
	    if (as_str != entry)
288 461
		rb_ary_store(features, i, as_str);
289 462
	    rb_str_freeze(as_str);
290
	    features_index_add(as_str, INT2FIX(i));
463
	    features_index_add(RSTRING_PTR(as_str), RSTRING_LEN(as_str), i);
291 464
	}
292 465
	reset_loaded_features_snapshot();
293 466
    }
294
    return vm->loaded_features_index;
467
    return get_loaded_features_index_raw();
295 468
}
296 469

  
297 470
/* This searches `load_path` for a value such that
......
371 544
static int
372 545
rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
373 546
{
374
    VALUE features, feature_val, this_feature_index = Qnil, v, p, load_path = 0;
547
    VALUE features,  v, p, load_path = 0;
375 548
    const char *f, *e;
376 549
    long i, len, elen, n;
377
    st_table *loading_tbl, *features_index;
550
    st_table *loading_tbl;
551
    st_features_index *features_index;
552
    fi_list_item index_list;
378 553
    st_data_t data;
379 554
    int type;
380 555

  
......
392 567
    features = get_loaded_features();
393 568
    features_index = get_loaded_features_index();
394 569

  
395
    feature_val = rb_str_new(feature, len);
396
    st_lookup(features_index, (st_data_t)feature, (st_data_t *)&this_feature_index);
570
    index_list = features_index_find(features_index, feature, len);
397 571
    /* We search `features` for an entry such that either
398 572
         "#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
399 573
       for some j, or
......
413 587
       form "#{feature}#{e}" are accepted.
414 588

  
415 589
       In `rb_provide_feature()` and `get_loaded_features_index()` we
416
       maintain an invariant that the array `this_feature_index` will
417
       point to every entry in `features` which has the form
590
       maintain an invariant that the list `index` will point to at least
591
       every entry in `features` which has the form
418 592
         "#{prefix}#{feature}#{e}"
419 593
       where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
420 594
       or ends in '/'.  This includes both match forms above, as well
421 595
       as any distractors, so we may ignore all other entries in `features`.
596
       (since we store only hash value of feature, there could be also
597
       other features in the list, but probability of it is very small,
598
       and loaded_feature_path will recheck for it)
422 599
     */
423
    if (!NIL_P(this_feature_index)) {
424
	for (i = 0; ; i++) {
425
	    VALUE entry;
426
	    long index;
427
	    if (RB_TYPE_P(this_feature_index, T_ARRAY)) {
428
		if (i >= RARRAY_LEN(this_feature_index)) break;
429
		entry = RARRAY_PTR(this_feature_index)[i];
430
	    }
431
	    else {
432
		if (i > 0) break;
433
		entry = this_feature_index;
434
	    }
435
	    index = FIX2LONG(entry);
600
    if (index_list.offset != FI_LAST) {
601
	for (; index_list.offset != FI_LAST ;
602
	       index_list = features_index_next(features_index, index_list)) {
603
	    long index = index_list.offset;
436 604

  
437 605
	    v = RARRAY_PTR(features)[index];
438 606
	    f = StringValuePtr(v);
......
548 716
    rb_str_freeze(feature);
549 717

  
550 718
    rb_ary_push(features, feature);
551
    features_index_add(feature, INT2FIX(RARRAY_LEN(features)-1));
719
    StringValue(feature);
720
    features_index_add(RSTRING_PTR(feature), RSTRING_LEN(feature), (int)(RARRAY_LEN(features)-1));
721
    RB_GC_GUARD(feature);
552 722
    reset_loaded_features_snapshot();
553 723
}
554 724

  
......
1134 1304
    rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
1135 1305
    vm->loaded_features = rb_ary_new();
1136 1306
    vm->loaded_features_snapshot = rb_ary_tmp_new(0);
1137
    vm->loaded_features_index = st_init_strtable();
1307
    vm->loaded_features_index = features_index_allocate();
1138 1308

  
1139 1309
    rb_define_global_function("load", rb_f_load, -1);
1140 1310
    rb_define_global_function("require", rb_f_require, 1);
vm.c
1572 1572
	if (vm->loading_table) {
1573 1573
	    rb_mark_tbl(vm->loading_table);
1574 1574
	}
1575
	if (vm->loaded_features_index) {
1576
	    rb_mark_tbl(vm->loaded_features_index);
1577
	}
1575
	RUBY_MARK_UNLESS_NULL(vm->loaded_features_index);
1578 1576

  
1579 1577
	rb_vm_trace_mark_event_hooks(&vm->event_hooks);
1580 1578

  
vm_core.h
367 367
    VALUE expanded_load_path;
368 368
    VALUE loaded_features;
369 369
    VALUE loaded_features_snapshot;
370
    struct st_table *loaded_features_index;
370
    VALUE loaded_features_index;
371 371
    struct st_table *loading_table;
372 372

  
373 373
    /* signal */
374
-