Skip to content

Commit 2156577

Browse files
committedMay 16, 2011
Add a special RubyArray subclass for LOADED_FEATURES, to speed up searches during Kernel#require.
1 parent c70879d commit 2156577

File tree

3 files changed

+392
-47
lines changed

3 files changed

+392
-47
lines changed
 

‎src/org/jruby/RubyArray.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ private RubyArray(Ruby runtime, RubyClass metaClass, IRubyObject[] vals, int beg
303303
this.isShared = true;
304304
}
305305

306-
private RubyArray(Ruby runtime, int length) {
306+
protected RubyArray(Ruby runtime, int length) {
307307
super(runtime, runtime.getArray());
308308
values = new IRubyObject[length];
309309
}

‎src/org/jruby/runtime/load/LoadService.java

+24-46
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@
3333
***** END LICENSE BLOCK *****/
3434
package org.jruby.runtime.load;
3535

36+
import org.jruby.util.collections.StringArraySet;
3637
import java.io.File;
3738
import java.io.FileNotFoundException;
3839
import java.io.IOException;
3940
import java.net.MalformedURLException;
4041
import java.net.URL;
4142
import java.util.ArrayList;
42-
import java.util.Collections;
4343
import java.util.HashMap;
4444
import java.util.Iterator;
4545
import java.util.List;
@@ -54,14 +54,14 @@
5454
import org.jruby.Ruby;
5555
import org.jruby.RubyArray;
5656
import org.jruby.RubyFile;
57-
import org.jruby.RubyFixnum;
5857
import org.jruby.RubyHash;
5958
import org.jruby.RubyInstanceConfig;
6059
import org.jruby.RubyString;
6160
import org.jruby.ast.executable.Script;
6261
import org.jruby.exceptions.MainExitException;
6362
import org.jruby.exceptions.RaiseException;
6463
import org.jruby.platform.Platform;
64+
import org.jruby.runtime.Block;
6565
import org.jruby.runtime.Constants;
6666
import org.jruby.runtime.builtin.IRubyObject;
6767
import org.jruby.util.JRubyFile;
@@ -156,8 +156,7 @@ public String[] getSuffixes() {
156156
protected static final Pattern extensionPattern = Pattern.compile("\\.(?:so|o|dll|bundle|jar)$");
157157

158158
protected RubyArray loadPath;
159-
protected RubyArray loadedFeatures;
160-
protected List loadedFeaturesInternal;
159+
protected StringArraySet loadedFeatures;
161160
protected final Map<String, Library> builtinLibraries = new HashMap<String, Library>();
162161

163162
protected final Map<String, JarFile> jarFiles = new HashMap<String, JarFile>();
@@ -179,8 +178,23 @@ public LoadService(Ruby runtime) {
179178

180179
public void init(List additionalDirectories) {
181180
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);
184198

185199
// add all startup load paths to the list first
186200
for (Iterator iter = additionalDirectories.iterator(); iter.hasNext();) {
@@ -200,7 +214,6 @@ public void init(List additionalDirectories) {
200214

201215
// wrap in try/catch for security exceptions in an applet
202216
try {
203-
String jrubyHome = runtime.getJRubyHome();
204217
if (jrubyHome != null) {
205218
char sep = '/';
206219
String rubyDir = jrubyHome + sep + "lib" + sep + "ruby" + sep;
@@ -217,18 +230,6 @@ public void init(List additionalDirectories) {
217230
addPath(rubyDir + "site_ruby" + sep + "shared");
218231
addPath(rubyDir + Constants.RUBY_MAJOR_VERSION);
219232
}
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) {}
232233
}
233234

234235
} catch(SecurityException ignore) {}
@@ -240,7 +241,7 @@ public void init(List additionalDirectories) {
240241
}
241242

242243
protected void addLoadedFeature(RubyString loadNameRubyString) {
243-
loadedFeaturesInternal.add(loadNameRubyString);
244+
loadedFeatures.append(loadNameRubyString);
244245
}
245246

246247
protected void addPath(String path) {
@@ -489,35 +490,12 @@ public void removeBuiltinLibrary(String name) {
489490
}
490491

491492
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);
505495
}
506496

507497
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();
521499
}
522500

523501
protected boolean isJarfileLibrary(SearchState state, final String file) {
Original file line numberDiff line numberDiff line change
@@ -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 commit comments

Comments
 (0)
Please sign in to comment.