Skip to content
This repository
Browse code

Add a special RubyArray subclass for LOADED_FEATURES, to speed up sea…

…rches during Kernel#require.
  • Loading branch information...
commit 21565774f8800b3df6f839505365d340b573f253 1 parent c70879d
Charles Oliver Nutter authored May 16, 2011
2  src/org/jruby/RubyArray.java
@@ -303,7 +303,7 @@ private RubyArray(Ruby runtime, RubyClass metaClass, IRubyObject[] vals, int beg
303 303
         this.isShared = true;
304 304
     }
305 305
     
306  
-    private RubyArray(Ruby runtime, int length) {
  306
+    protected RubyArray(Ruby runtime, int length) {
307 307
         super(runtime, runtime.getArray());
308 308
         values = new IRubyObject[length];
309 309
     }
70  src/org/jruby/runtime/load/LoadService.java
@@ -33,13 +33,13 @@
33 33
  ***** END LICENSE BLOCK *****/
34 34
 package org.jruby.runtime.load;
35 35
 
  36
+import org.jruby.util.collections.StringArraySet;
36 37
 import java.io.File;
37 38
 import java.io.FileNotFoundException;
38 39
 import java.io.IOException;
39 40
 import java.net.MalformedURLException;
40 41
 import java.net.URL;
41 42
 import java.util.ArrayList;
42  
-import java.util.Collections;
43 43
 import java.util.HashMap;
44 44
 import java.util.Iterator;
45 45
 import java.util.List;
@@ -54,7 +54,6 @@
54 54
 import org.jruby.Ruby;
55 55
 import org.jruby.RubyArray;
56 56
 import org.jruby.RubyFile;
57  
-import org.jruby.RubyFixnum;
58 57
 import org.jruby.RubyHash;
59 58
 import org.jruby.RubyInstanceConfig;
60 59
 import org.jruby.RubyString;
@@ -62,6 +61,7 @@
62 61
 import org.jruby.exceptions.MainExitException;
63 62
 import org.jruby.exceptions.RaiseException;
64 63
 import org.jruby.platform.Platform;
  64
+import org.jruby.runtime.Block;
65 65
 import org.jruby.runtime.Constants;
66 66
 import org.jruby.runtime.builtin.IRubyObject;
67 67
 import org.jruby.util.JRubyFile;
@@ -156,8 +156,7 @@
156 156
     protected static final Pattern extensionPattern = Pattern.compile("\\.(?:so|o|dll|bundle|jar)$");
157 157
 
158 158
     protected RubyArray loadPath;
159  
-    protected RubyArray loadedFeatures;
160  
-    protected List loadedFeaturesInternal;
  159
+    protected StringArraySet loadedFeatures;
161 160
     protected final Map<String, Library> builtinLibraries = new HashMap<String, Library>();
162 161
 
163 162
     protected final Map<String, JarFile> jarFiles = new HashMap<String, JarFile>();
@@ -179,8 +178,23 @@ public LoadService(Ruby runtime) {
179 178
 
180 179
     public void init(List additionalDirectories) {
181 180
         loadPath = RubyArray.newArray(runtime);
182  
-        loadedFeatures = RubyArray.newArray(runtime);
183  
-        loadedFeaturesInternal = Collections.synchronizedList(loadedFeatures);
  181
+        
  182
+        String jrubyHome = runtime.getJRubyHome();
  183
+        if (jrubyHome != null) {
  184
+            String lowerCaseJRubyHome = jrubyHome.toLowerCase();
  185
+            String upperCaseJRubyHome = lowerCaseJRubyHome.toUpperCase();
  186
+
  187
+            try {
  188
+                String canonNormal = new File(jrubyHome).getCanonicalPath();
  189
+                String canonLower = new File(lowerCaseJRubyHome).getCanonicalPath();
  190
+                String canonUpper = new File(upperCaseJRubyHome).getCanonicalPath();
  191
+                if (canonNormal.equals(canonLower) && canonLower.equals(canonUpper)) {
  192
+                    caseInsensitiveFS = true;
  193
+                }
  194
+            } catch (Exception e) {}
  195
+        }
  196
+        
  197
+        loadedFeatures = new StringArraySet(runtime, caseInsensitiveFS);
184 198
         
185 199
         // add all startup load paths to the list first
186 200
         for (Iterator iter = additionalDirectories.iterator(); iter.hasNext();) {
@@ -200,7 +214,6 @@ public void init(List additionalDirectories) {
200 214
 
201 215
         // wrap in try/catch for security exceptions in an applet
202 216
         try {
203  
-            String jrubyHome = runtime.getJRubyHome();
204 217
             if (jrubyHome != null) {
205 218
                 char sep = '/';
206 219
                 String rubyDir = jrubyHome + sep + "lib" + sep + "ruby" + sep;
@@ -217,18 +230,6 @@ public void init(List additionalDirectories) {
217 230
                     addPath(rubyDir + "site_ruby" + sep + "shared");
218 231
                     addPath(rubyDir + Constants.RUBY_MAJOR_VERSION);
219 232
                 }
220  
-
221  
-                String lowerCaseJRubyHome = jrubyHome.toLowerCase();
222  
-                String upperCaseJRubyHome = lowerCaseJRubyHome.toUpperCase();
223  
-
224  
-                try {
225  
-                    String canonNormal = new File(jrubyHome).getCanonicalPath();
226  
-                    String canonLower = new File(lowerCaseJRubyHome).getCanonicalPath();
227  
-                    String canonUpper = new File(upperCaseJRubyHome).getCanonicalPath();
228  
-                    if (canonNormal.equals(canonLower) && canonLower.equals(canonUpper)) {
229  
-                        caseInsensitiveFS = true;
230  
-                    }
231  
-                } catch (Exception e) {}
232 233
             }
233 234
 
234 235
         } catch(SecurityException ignore) {}
@@ -240,7 +241,7 @@ public void init(List additionalDirectories) {
240 241
     }
241 242
 
242 243
     protected void addLoadedFeature(RubyString loadNameRubyString) {
243  
-        loadedFeaturesInternal.add(loadNameRubyString);
  244
+        loadedFeatures.append(loadNameRubyString);
244 245
     }
245 246
 
246 247
     protected void addPath(String path) {
@@ -489,35 +490,12 @@ public void removeBuiltinLibrary(String name) {
489 490
     }
490 491
 
491 492
     public void removeInternalLoadedFeature(String name) {
492  
-        if (caseInsensitiveFS) {
493  
-            // on a case-insensitive filesystem, we need to search case-insensitively
494  
-            // to remove the loaded feature
495  
-            RubyString nameRubyString = runtime.newString(name);
496  
-            for (int i = 0; i < loadedFeatures.size(); i++) {
497  
-                RubyString feature = loadedFeatures.eltInternal(i).convertToString();
498  
-                if (((RubyFixnum)feature.casecmp(runtime.getCurrentContext(), nameRubyString)).getLongValue() == 0) {
499  
-                    loadedFeatures.remove(i);
500  
-                }
501  
-            }
502  
-        } else {
503  
-            loadedFeaturesInternal.remove(name);
504  
-        }
  493
+        RubyString nameRubyString = runtime.newString(name);
  494
+        loadedFeatures.delete(runtime.getCurrentContext(), nameRubyString, Block.NULL_BLOCK);
505 495
     }
506 496
 
507 497
     protected boolean featureAlreadyLoaded(RubyString loadNameRubyString) {
508  
-        if (caseInsensitiveFS) {
509  
-            // on a case-insensitive filesystem, we need to search case-insensitively
510  
-            // to find the loaded feature
511  
-            for (int i = 0; i < loadedFeatures.size(); i++) {
512  
-                RubyString feature = loadedFeatures.eltInternal(i).convertToString();
513  
-                if (((RubyFixnum)feature.casecmp(runtime.getCurrentContext(), loadNameRubyString)).getLongValue() == 0) {
514  
-                    return true;
515  
-                }
516  
-            }
517  
-            return false;
518  
-        } else {
519  
-            return loadedFeaturesInternal.contains(loadNameRubyString);
520  
-        }
  498
+        return loadedFeatures.include_p(runtime.getCurrentContext(), loadNameRubyString).isTrue();
521 499
     }
522 500
 
523 501
     protected boolean isJarfileLibrary(SearchState state, final String file) {
367  src/org/jruby/util/collections/StringArraySet.java
... ...
@@ -0,0 +1,367 @@
  1
+/*
  2
+ ***** BEGIN LICENSE BLOCK *****
  3
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  4
+ *
  5
+ * The contents of this file are subject to the Common Public
  6
+ * License Version 1.0 (the "License"); you may not use this file
  7
+ * except in compliance with the License. You may obtain a copy of
  8
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
  9
+ *
  10
+ * Software distributed under the License is distributed on an "AS
  11
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12
+ * implied. See the License for the specific language governing
  13
+ * rights and limitations under the License.
  14
+ *
  15
+ * Copyright (C) 2011 Charles O Nutter <headius@headius.com>
  16
+ *
  17
+ * Alternatively, the contents of this file may be used under the terms of
  18
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
  19
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  20
+ * in which case the provisions of the GPL or the LGPL are applicable instead
  21
+ * of those above. If you wish to allow use of your version of this file only
  22
+ * under the terms of either the GPL or the LGPL, and not to allow others to
  23
+ * use your version of this file under the terms of the CPL, indicate your
  24
+ * decision by deleting the provisions above and replace them with the notice
  25
+ * and other provisions required by the GPL or the LGPL. If you do not delete
  26
+ * the provisions above, a recipient may use your version of this file under
  27
+ * the terms of any one of the CPL, the GPL or the LGPL.
  28
+ ***** END LICENSE BLOCK *****/
  29
+package org.jruby.util.collections;
  30
+
  31
+import java.util.HashSet;
  32
+import java.util.Set;
  33
+import org.jruby.Ruby;
  34
+import org.jruby.RubyArray;
  35
+import org.jruby.RubyBoolean;
  36
+import org.jruby.runtime.Block;
  37
+import org.jruby.runtime.ThreadContext;
  38
+import org.jruby.runtime.builtin.IRubyObject;
  39
+
  40
+/**
  41
+ * An optionally case-insensitive RubyArray that maintains an O(1) Set for fast
  42
+ * include? operations.
  43
+ */
  44
+public class StringArraySet extends RubyArray {
  45
+    private final Set<String> set = new HashSet<String>();
  46
+    private final boolean caseInsensitive;
  47
+
  48
+    public StringArraySet(Ruby runtime, boolean caseInsensitive) {
  49
+        super(runtime, 4);
  50
+        this.caseInsensitive = caseInsensitive;
  51
+    }
  52
+
  53
+    @Override
  54
+    public synchronized RubyArray append(IRubyObject item) {
  55
+        String string = getStringFromItem(item);
  56
+        RubyArray result = super.append(item);
  57
+        set.add(string);
  58
+        return result;
  59
+    }
  60
+
  61
+    @Override
  62
+    public synchronized void clear() {
  63
+        super.clear();
  64
+        set.clear();
  65
+    }
  66
+
  67
+    @Override
  68
+    public synchronized IRubyObject delete(ThreadContext context, IRubyObject item, Block block) {
  69
+        String string = getStringFromItem(item);
  70
+        IRubyObject result = super.delete(context, item, block);
  71
+        set.remove(string);
  72
+        return result;
  73
+    }
  74
+
  75
+    @Override
  76
+    public synchronized IRubyObject delete_if(ThreadContext context, Block block) {
  77
+        IRubyObject result = super.delete_if(context, block);
  78
+        rehash();
  79
+        return result;
  80
+    }
  81
+
  82
+    @Override
  83
+    public synchronized RubyBoolean include_p(ThreadContext context, IRubyObject item) {
  84
+        return context.runtime.newBoolean(set.contains(getStringFromItem(item)));
  85
+    }
  86
+
  87
+    @Override
  88
+    public synchronized IRubyObject replace(IRubyObject orig) {
  89
+        IRubyObject result = super.replace(orig);
  90
+        rehash();
  91
+        return result;
  92
+    }
  93
+
  94
+    @Override
  95
+    public synchronized IRubyObject unshift(IRubyObject item) {
  96
+        String string = getStringFromItem(item);
  97
+        IRubyObject result = super.unshift(item);
  98
+        set.add(string);
  99
+        return result;
  100
+    }
  101
+
  102
+    @Override
  103
+    public synchronized IRubyObject unshift(IRubyObject[] items) {
  104
+        IRubyObject result = super.unshift(items);
  105
+        putAll(toJavaArray());
  106
+        return result;
  107
+    }
  108
+
  109
+    @Override
  110
+    public synchronized IRubyObject aset(IRubyObject arg0, IRubyObject arg1) {
  111
+        IRubyObject result = super.aset(arg0, arg1);
  112
+        rehash();
  113
+        return result;
  114
+    }
  115
+
  116
+    @Override
  117
+    public synchronized IRubyObject aset(IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  118
+        IRubyObject result = super.aset(arg0, arg1, arg2);
  119
+        rehash();
  120
+        return result;
  121
+    }
  122
+
  123
+    @Override
  124
+    public synchronized IRubyObject aset19(IRubyObject arg0, IRubyObject arg1) {
  125
+        IRubyObject result = super.aset19(arg0, arg1);
  126
+        rehash();
  127
+        return result;
  128
+    }
  129
+
  130
+    @Override
  131
+    public synchronized RubyArray collectBang(ThreadContext context, Block block) {
  132
+        RubyArray result = super.collectBang(context, block);
  133
+        rehash();
  134
+        return result;
  135
+    }
  136
+
  137
+    @Override
  138
+    public synchronized IRubyObject collect_bang(ThreadContext context, Block block) {
  139
+        IRubyObject result = super.collect_bang(context, block);
  140
+        rehash();
  141
+        return result;
  142
+    }
  143
+
  144
+    @Override
  145
+    public synchronized IRubyObject compact() {
  146
+        IRubyObject result = super.compact();
  147
+        rehash();
  148
+        return result;
  149
+    }
  150
+
  151
+    @Override
  152
+    public synchronized IRubyObject drop(ThreadContext context, IRubyObject n) {
  153
+        IRubyObject result = super.drop(context, n);
  154
+        rehash();
  155
+        return result;
  156
+    }
  157
+
  158
+    @Override
  159
+    public synchronized IRubyObject drop_while(ThreadContext context, Block block) {
  160
+        IRubyObject result = super.drop_while(context, block);
  161
+        rehash();
  162
+        return result;
  163
+    }
  164
+
  165
+    @Override
  166
+    public synchronized IRubyObject flatten_bang(ThreadContext context) {
  167
+        IRubyObject result = super.flatten_bang(context);
  168
+        rehash();
  169
+        return result;
  170
+    }
  171
+
  172
+    @Override
  173
+    public synchronized IRubyObject flatten_bang(ThreadContext context, IRubyObject arg) {
  174
+        IRubyObject result = super.flatten_bang(context, arg);
  175
+        rehash();
  176
+        return result;
  177
+    }
  178
+
  179
+    @Override
  180
+    public synchronized IRubyObject flatten_bang19(ThreadContext context) {
  181
+        IRubyObject result = super.flatten_bang19(context);
  182
+        rehash();
  183
+        return result;
  184
+    }
  185
+
  186
+    @Override
  187
+    public synchronized IRubyObject flatten_bang19(ThreadContext context, IRubyObject arg) {
  188
+        IRubyObject result = super.flatten_bang19(context, arg);
  189
+        rehash();
  190
+        return result;
  191
+    }
  192
+
  193
+    @Override
  194
+    public synchronized IRubyObject insert() {
  195
+        IRubyObject result = super.insert();
  196
+        rehash();
  197
+        return result;
  198
+    }
  199
+
  200
+    @Override
  201
+    public synchronized IRubyObject insert(IRubyObject arg) {
  202
+        IRubyObject result = super.insert(arg);
  203
+        rehash();
  204
+        return result;
  205
+    }
  206
+
  207
+    @Override
  208
+    public synchronized IRubyObject insert(IRubyObject arg1, IRubyObject arg2) {
  209
+        IRubyObject result = super.insert(arg1, arg2);
  210
+        rehash();
  211
+        return result;
  212
+    }
  213
+
  214
+    @Override
  215
+    public synchronized IRubyObject insert(IRubyObject[] args) {
  216
+        IRubyObject result = super.insert(args);
  217
+        rehash();
  218
+        return result;
  219
+    }
  220
+
  221
+    @Override
  222
+    public synchronized IRubyObject insert19(IRubyObject arg) {
  223
+        IRubyObject result = super.insert19(arg);
  224
+        rehash();
  225
+        return result;
  226
+    }
  227
+
  228
+    @Override
  229
+    public synchronized IRubyObject insert19(IRubyObject arg1, IRubyObject arg2) {
  230
+        IRubyObject result = super.insert19(arg1, arg2);
  231
+        rehash();
  232
+        return result;
  233
+    }
  234
+
  235
+    @Override
  236
+    public synchronized IRubyObject insert19(IRubyObject[] args) {
  237
+        IRubyObject result = super.insert19(args);
  238
+        rehash();
  239
+        return result;
  240
+    }
  241
+
  242
+    @Override
  243
+    public synchronized IRubyObject map_bang(ThreadContext context, Block block) {
  244
+        IRubyObject result = super.map_bang(context, block);
  245
+        rehash();
  246
+        return result;
  247
+    }
  248
+
  249
+    @Override
  250
+    public synchronized IRubyObject pop(ThreadContext context) {
  251
+        IRubyObject result = super.pop(context);
  252
+        rehash();
  253
+        return result;
  254
+    }
  255
+
  256
+    @Override
  257
+    public synchronized IRubyObject pop(ThreadContext context, IRubyObject num) {
  258
+        IRubyObject result = super.pop(context, num);
  259
+        rehash();
  260
+        return result;
  261
+    }
  262
+
  263
+    @Override
  264
+    public synchronized RubyArray push_m(IRubyObject[] items) {
  265
+        RubyArray result = super.push_m(items);
  266
+        rehash();
  267
+        return result;
  268
+    }
  269
+
  270
+    @Override
  271
+    public synchronized RubyArray push_m19(IRubyObject[] items) {
  272
+        RubyArray result = super.push_m19(items);
  273
+        rehash();
  274
+        return result;
  275
+    }
  276
+
  277
+    @Override
  278
+    public synchronized IRubyObject rejectBang(ThreadContext context, Block block) {
  279
+        IRubyObject result = super.rejectBang(context, block);
  280
+        rehash();
  281
+        return result;
  282
+    }
  283
+
  284
+    @Override
  285
+    public synchronized IRubyObject reject_bang(ThreadContext context, Block block) {
  286
+        IRubyObject result = super.reject_bang(context, block);
  287
+        rehash();
  288
+        return result;
  289
+    }
  290
+
  291
+    @Override
  292
+    public synchronized IRubyObject select_bang(ThreadContext context, Block block) {
  293
+        IRubyObject result = super.select_bang(context, block);
  294
+        rehash();
  295
+        return result;
  296
+    }
  297
+
  298
+    @Override
  299
+    public synchronized IRubyObject shift(ThreadContext context) {
  300
+        IRubyObject result = super.shift(context);
  301
+        rehash();
  302
+        return result;
  303
+    }
  304
+
  305
+    @Override
  306
+    public synchronized IRubyObject shift(ThreadContext context, IRubyObject num) {
  307
+        IRubyObject result = super.shift(context, num);
  308
+        rehash();
  309
+        return result;
  310
+    }
  311
+
  312
+    @Override
  313
+    public synchronized IRubyObject slice_bang(IRubyObject arg0) {
  314
+        IRubyObject result = super.slice_bang(arg0);
  315
+        rehash();
  316
+        return result;
  317
+    }
  318
+
  319
+    @Override
  320
+    public synchronized IRubyObject slice_bang(IRubyObject arg0, IRubyObject arg1) {
  321
+        IRubyObject result = super.slice_bang(arg0, arg1);
  322
+        rehash();
  323
+        return result;
  324
+    }
  325
+
  326
+    @Override
  327
+    public synchronized IRubyObject unshift() {
  328
+        IRubyObject result = super.unshift();
  329
+        rehash();
  330
+        return result;
  331
+    }
  332
+
  333
+    @Override
  334
+    public synchronized IRubyObject unshift19() {
  335
+        IRubyObject result = super.unshift19();
  336
+        rehash();
  337
+        return result;
  338
+    }
  339
+
  340
+    @Override
  341
+    public synchronized IRubyObject unshift19(IRubyObject item) {
  342
+        IRubyObject result = super.unshift19(item);
  343
+        rehash();
  344
+        return result;
  345
+    }
  346
+
  347
+    private String getStringFromItem(IRubyObject item) {
  348
+        String string = item.convertToString().asJavaString();
  349
+        if (caseInsensitive) {
  350
+            string = string.toLowerCase();
  351
+        }
  352
+        return string;
  353
+    }
  354
+
  355
+    private void rehash() {
  356
+        set.clear();
  357
+        putAll(toJavaArray());
  358
+    }
  359
+
  360
+    private void putAll(IRubyObject[] items) {
  361
+        for (IRubyObject item : items) {
  362
+            String string = getStringFromItem(item);
  363
+            set.add(string);
  364
+        }
  365
+    }
  366
+    
  367
+}

0 notes on commit 2156577

Please sign in to comment.
Something went wrong with that request. Please try again.