summaryrefslogtreecommitdiff
path: root/load.c
diff options
context:
space:
mode:
authorshirosaki <shirosaki@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-05 15:27:01 +0000
committershirosaki <shirosaki@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-05 15:27:01 +0000
commitb56a2afc14ebdc2e5524e0a8ebcf920626d463af (patch)
treed5ed0e2b7538ecddfebbebeea6949ace96c4aec7 /load.c
parent2a7d349eb4d8535dda1f615a262614d3cee4d038 (diff)
Index $LOADED_FEATURES so that require isn't so slow
* load.c (rb_feature_p, rb_provide_feature): index $LOADED_FEATURES so that require isn't so slow. * load.c (rb_provide_feature, get_loaded_features_index): ensure that $LOADED_FEATURES entries are frozen strings. The user must mutate $LOADED_FEATURES itself rather than its individual entries. * load.c (reset_loaded_features_snapshot): add a new function to reset vm->loaded_features_snapshot. * load.c (get_loaded_features_index_raw): add a new function to get the loaded-features index. * load.c (features_index_add_single): add a new function to add to the loaded-features index a single feature. * load.c (features_index_add): add a new function to add to the loaded-features index all the required entries for `feature`. * vm_core.h (rb_vm_struct): add fields. * vm.c (rb_vm_mark): mark new fields. * include/ruby/intern.h (rb_hash_clear): declare function. * hash.c (rb_hash_clear): make function non-static. Patch by Greg Price. [ruby-core:47970] [Bug #7158] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37480 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'load.c')
-rw-r--r--load.c169
1 files changed, 144 insertions, 25 deletions
diff --git a/load.c b/load.c
index 54629983e8..8c302487a1 100644
--- a/load.c
+++ b/load.c
@@ -18,7 +18,6 @@ VALUE ruby_dln_librefs;
#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
#endif
-
static const char *const loadable_ext[] = {
".rb", DLEXT,
#ifdef DLEXT2
@@ -63,12 +62,110 @@ get_loaded_features(void)
return GET_VM()->loaded_features;
}
+static void
+reset_loaded_features_snapshot(void)
+{
+ rb_vm_t *vm = GET_VM();
+ rb_ary_replace(vm->loaded_features_snapshot, vm->loaded_features);
+}
+
+static VALUE
+get_loaded_features_index_raw(void)
+{
+ return GET_VM()->loaded_features_index;
+}
+
static st_table *
get_loading_table(void)
{
return GET_VM()->loading_table;
}
+static void
+features_index_add_single(VALUE short_feature, VALUE offset)
+{
+ VALUE features_index, this_feature_index;
+ features_index = get_loaded_features_index_raw();
+ if ((this_feature_index = rb_hash_lookup(features_index, short_feature)) == Qnil) {
+ this_feature_index = rb_ary_new();
+ rb_hash_aset(features_index, short_feature, this_feature_index);
+ }
+ rb_ary_push(this_feature_index, offset);
+}
+
+/* Add to the loaded-features index all the required entries for
+ `feature`, located at `offset` in $LOADED_FEATURES. We add an
+ index entry at each string `short_feature` for which
+ feature == "#{prefix}#{short_feature}#{e}"
+ where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
+ or ends in '/'. This maintains the invariant that `rb_feature_p()`
+ relies on for its fast lookup.
+*/
+static void
+features_index_add(VALUE feature, VALUE offset)
+{
+ VALUE short_feature;
+ const char *feature_str, *feature_end, *ext, *p;
+
+ feature_str = StringValuePtr(feature);
+ feature_end = feature_str + RSTRING_LEN(feature);
+
+ for (ext = feature_end; ext > feature_str; ext--)
+ if (*ext == '.' || *ext == '/')
+ break;
+ if (*ext != '.')
+ ext = NULL;
+ /* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
+ at the end of `feature`, or is NULL if there is no such string. */
+
+ p = ext ? ext : feature_end;
+ while (1) {
+ p--;
+ while (p >= feature_str && *p != '/')
+ p--;
+ if (p < feature_str)
+ break;
+ /* Now *p == '/'. We reach this point for every '/' in `feature`. */
+ short_feature = rb_str_substr(feature, p + 1 - feature_str, feature_end - p - 1);
+ features_index_add_single(short_feature, offset);
+ if (ext) {
+ short_feature = rb_str_substr(feature, p + 1 - feature_str, ext - p - 1);
+ features_index_add_single(short_feature, offset);
+ }
+ }
+ features_index_add_single(feature, offset);
+ if (ext) {
+ short_feature = rb_str_substr(feature, 0, ext - feature_str);
+ features_index_add_single(short_feature, offset);
+ }
+}
+
+static VALUE
+get_loaded_features_index(void)
+{
+ VALUE features;
+ int i;
+ rb_vm_t *vm = GET_VM();
+
+ if (!rb_ary_shared_with_p(vm->loaded_features_snapshot, vm->loaded_features)) {
+ /* The sharing was broken; something (other than us in rb_provide_feature())
+ modified loaded_features. Rebuild the index. */
+ rb_hash_clear(vm->loaded_features_index);
+ features = vm->loaded_features;
+ for (i = 0; i < RARRAY_LEN(features); i++) {
+ VALUE entry, as_str;
+ as_str = entry = rb_ary_entry(features, i);
+ StringValue(as_str);
+ if (as_str != entry)
+ rb_ary_store(features, i, as_str);
+ rb_str_freeze(as_str);
+ features_index_add(as_str, INT2FIX(i));
+ }
+ reset_loaded_features_snapshot();
+ }
+ return vm->loaded_features_index;
+}
+
/* This searches `load_path` for a value such that
name == "#{load_path[i]}/#{feature}"
if `feature` is a suffix of `name`, or otherwise
@@ -143,7 +240,7 @@ loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
static int
rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
{
- VALUE v, features, p, load_path = 0;
+ VALUE features, features_index, feature_val, this_feature_index, v, p, load_path = 0;
const char *f, *e;
long i, len, elen, n;
st_table *loading_tbl;
@@ -162,27 +259,39 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
type = 0;
}
features = get_loaded_features();
- for (i = 0; i < RARRAY_LEN(features); ++i) {
- /* This loop searches `features` for an entry such that either
- "#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
- for some j, or
- "#{features[i]}" == "#{feature}#{e}"
- Here `e` is an "allowed" extension -- either empty or one
- of the extensions accepted by IS_RBEXT, IS_SOEXT, or
- IS_DLEXT. Further, if `ext && rb` then `IS_RBEXT(e)`,
- and if `ext && !rb` then `IS_SOEXT(e) || IS_DLEXT(e)`.
-
- If `expanded`, then only the latter form (without
- load_path[j]) is accepted. Otherwise either form is
- accepted, *unless* `ext` is false and an otherwise-matching
- entry of the first form is preceded by an entry of the form
- "#{features[i2]}" == "#{load_path[j2]}/#{feature}#{e2}"
- where `e2` matches /^\.[^./]*$/ but is not an allowed extension.
- After a "distractor" entry of this form, only entries of the
- form "#{feature}#{e}" are accepted.
- */
-
- v = RARRAY_PTR(features)[i];
+ features_index = get_loaded_features_index();
+
+ feature_val = rb_str_new(feature, len);
+ this_feature_index = rb_hash_lookup(features_index, feature_val);
+ /* We search `features` for an entry such that either
+ "#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
+ for some j, or
+ "#{features[i]}" == "#{feature}#{e}"
+ Here `e` is an "allowed" extension -- either empty or one
+ of the extensions accepted by IS_RBEXT, IS_SOEXT, or
+ IS_DLEXT. Further, if `ext && rb` then `IS_RBEXT(e)`,
+ and if `ext && !rb` then `IS_SOEXT(e) || IS_DLEXT(e)`.
+
+ If `expanded`, then only the latter form (without load_path[j])
+ is accepted. Otherwise either form is accepted, *unless* `ext`
+ is false and an otherwise-matching entry of the first form is
+ preceded by an entry of the form
+ "#{features[i2]}" == "#{load_path[j2]}/#{feature}#{e2}"
+ where `e2` matches %r{^\.[^./]*$} but is not an allowed extension.
+ After a "distractor" entry of this form, only entries of the
+ form "#{feature}#{e}" are accepted.
+
+ In `rb_provide_feature()` and `get_loaded_features_index()` we
+ maintain an invariant that the array `this_feature_index` will
+ point to every entry in `features` which has the form
+ "#{prefix}#{feature}#{e}"
+ where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
+ or ends in '/'. This includes both match forms above, as well
+ as any distractors, so we may ignore all other entries in `features`.
+ */
+ for (i = 0; this_feature_index != Qnil && i < RARRAY_LEN(this_feature_index); i++) {
+ long index = FIX2LONG(rb_ary_entry(this_feature_index, i));
+ v = RARRAY_PTR(features)[index];
f = StringValuePtr(v);
if ((n = RSTRING_LEN(v)) < len) continue;
if (strncmp(f, feature, len) != 0) {
@@ -205,6 +314,7 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
return 'r';
}
}
+
loading_tbl = get_loading_table();
if (loading_tbl) {
f = 0;
@@ -284,11 +394,18 @@ rb_feature_provided(const char *feature, const char **loading)
static void
rb_provide_feature(VALUE feature)
{
- if (OBJ_FROZEN(get_loaded_features())) {
+ VALUE features;
+
+ features = get_loaded_features();
+ if (OBJ_FROZEN(features)) {
rb_raise(rb_eRuntimeError,
"$LOADED_FEATURES is frozen; cannot append feature");
}
- rb_ary_push(get_loaded_features(), feature);
+ rb_str_freeze(feature);
+
+ rb_ary_push(features, feature);
+ features_index_add(feature, INT2FIX(RARRAY_LEN(features)-1));
+ reset_loaded_features_snapshot();
}
void
@@ -829,6 +946,8 @@ Init_load()
rb_define_virtual_variable("$\"", get_loaded_features, 0);
rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
vm->loaded_features = rb_ary_new();
+ vm->loaded_features_snapshot = rb_ary_new();
+ vm->loaded_features_index = rb_hash_new();
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);