Browse Source

Index config section and subsection names in one pass

Instead of indexing the subsection names on each request for a given
section name, index both the section and subsection names in a single
scan through the entry list. This should improve lookup time for
reading the section names out of the configuration, especially for the
url.*.insteadof type of processing performed in RemoteConfig.

Change-Id: I7b3269565b1308f69d20dc3f3fe917aea00f8a73
stable-2.0
Shawn O. Pearce 13 years ago
parent
commit
c0b1443926
  1. 2
      org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
  2. 158
      org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
  3. 126
      org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java

2
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java

@ -414,9 +414,9 @@ public class ConfigTest {
names.contains("repositoryformatversion")); names.contains("repositoryformatversion"));
Iterator<String> itr = names.iterator(); Iterator<String> itr = names.iterator();
assertEquals("repositoryFormatVersion", itr.next());
assertEquals("filemode", itr.next()); assertEquals("filemode", itr.next());
assertEquals("logAllRefUpdates", itr.next()); assertEquals("logAllRefUpdates", itr.next());
assertEquals("repositoryFormatVersion", itr.next());
assertFalse(itr.hasNext()); assertFalse(itr.hasNext());
} }

158
org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java

@ -52,14 +52,9 @@
package org.eclipse.jgit.lib; package org.eclipse.jgit.lib;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.AbstractSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -479,17 +474,22 @@ public class Config {
* section to search for. * section to search for.
* @return set of all subsections of specified section within this * @return set of all subsections of specified section within this
* configuration and its base configuration; may be empty if no * configuration and its base configuration; may be empty if no
* subsection exists. * subsection exists. The set's iterator returns sections in the
* order they are declared by the configuration starting from this
* instance and progressing through the base.
*/ */
public Set<String> getSubsections(final String section) { public Set<String> getSubsections(final String section) {
return get(new SubsectionNames(section)); return getState().getSubsections(section);
} }
/** /**
* @return the sections defined in this {@link Config} * @return the sections defined in this {@link Config}. The set's iterator
* returns sections in the order they are declared by the
* configuration starting from this instance and progressing through
* the base.
*/ */
public Set<String> getSections() { public Set<String> getSections() {
return get(new SectionNames()); return getState().getSections();
} }
/** /**
@ -509,7 +509,7 @@ public class Config {
* @return the list of names defined for this subsection * @return the list of names defined for this subsection
*/ */
public Set<String> getNames(String section, String subsection) { public Set<String> getNames(String section, String subsection) {
return get(new NamesInSection(section, subsection)); return getState().getNames(section, subsection);
} }
/** /**
@ -1243,144 +1243,6 @@ public class Config {
T parse(Config cfg); T parse(Config cfg);
} }
private static class SubsectionNames implements SectionParser<Set<String>> {
private final String section;
SubsectionNames(final String sectionName) {
section = sectionName;
}
public int hashCode() {
return section.hashCode();
}
public boolean equals(Object other) {
if (other instanceof SubsectionNames) {
return section.equals(((SubsectionNames) other).section);
}
return false;
}
public Set<String> parse(Config cfg) {
final Set<String> result = new LinkedHashSet<String>();
while (cfg != null) {
for (final ConfigLine e : cfg.state.get().entryList) {
if (e.subsection != null && e.name == null
&& StringUtils.equalsIgnoreCase(section, e.section))
result.add(e.subsection);
}
cfg = cfg.baseConfig;
}
return Collections.unmodifiableSet(result);
}
}
private static class NamesInSection implements SectionParser<Set<String>> {
private final String section;
private final String subsection;
NamesInSection(final String sectionName, final String subSectionName) {
section = sectionName;
subsection = subSectionName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + section.hashCode();
result = prime * result
+ ((subsection == null) ? 0 : subsection.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NamesInSection other = (NamesInSection) obj;
if (!section.equals(other.section))
return false;
if (subsection == null) {
if (other.subsection != null)
return false;
} else if (!subsection.equals(other.subsection))
return false;
return true;
}
public Set<String> parse(Config cfg) {
final Map<String, String> m = new LinkedHashMap<String, String>();
while (cfg != null) {
for (final ConfigLine e : cfg.state.get().entryList) {
if (e.name == null)
continue;
if (!StringUtils.equalsIgnoreCase(section, e.section))
continue;
if ((subsection == null && e.subsection == null)
|| (subsection != null && subsection
.equals(e.subsection))) {
String lc = StringUtils.toLowerCase(e.name);
if (!m.containsKey(lc))
m.put(lc, e.name);
}
}
cfg = cfg.baseConfig;
}
return new CaseFoldingSet(m);
}
}
private static class SectionNames implements SectionParser<Set<String>> {
public Set<String> parse(Config cfg) {
final Map<String, String> m = new LinkedHashMap<String, String>();
while (cfg != null) {
for (final ConfigLine e : cfg.state.get().entryList) {
if (e.section != null) {
String lc = StringUtils.toLowerCase(e.section);
if (!m.containsKey(lc))
m.put(lc, e.section);
}
}
cfg = cfg.baseConfig;
}
return new CaseFoldingSet(m);
}
}
private static class CaseFoldingSet extends AbstractSet<String> {
private final Map<String, String> names;
CaseFoldingSet(Map<String, String> names) {
this.names = Collections.unmodifiableMap(names);
}
@Override
public boolean contains(Object needle) {
if (!(needle instanceof String))
return false;
String n = (String) needle;
return names.containsKey(n)
|| names.containsKey(StringUtils.toLowerCase(n));
}
@Override
public Iterator<String> iterator() {
return names.values().iterator();
}
@Override
public int size() {
return names.size();
}
}
private static class StringReader { private static class StringReader {
private final char[] buf; private final char[] buf;

126
org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java

@ -52,19 +52,29 @@ package org.eclipse.jgit.lib;
import static org.eclipse.jgit.util.StringUtils.compareIgnoreCase; import static org.eclipse.jgit.util.StringUtils.compareIgnoreCase;
import static org.eclipse.jgit.util.StringUtils.compareWithCase; import static org.eclipse.jgit.util.StringUtils.compareWithCase;
import static org.eclipse.jgit.util.StringUtils.toLowerCase;
import java.util.AbstractSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.util.StringUtils;
class ConfigSnapshot { class ConfigSnapshot {
final List<ConfigLine> entryList; final List<ConfigLine> entryList;
final Map<Object, Object> cache; final Map<Object, Object> cache;
final ConfigSnapshot baseState; final ConfigSnapshot baseState;
volatile List<ConfigLine> sorted; volatile List<ConfigLine> sorted;
volatile SectionNames names;
ConfigSnapshot(List<ConfigLine> entries, ConfigSnapshot base) { ConfigSnapshot(List<ConfigLine> entries, ConfigSnapshot base) {
entryList = entries; entryList = entries;
@ -72,6 +82,40 @@ class ConfigSnapshot {
baseState = base; baseState = base;
} }
Set<String> getSections() {
return names().sections;
}
Set<String> getSubsections(String section) {
Map<String, Set<String>> m = names().subsections;
Set<String> r = m.get(section);
if (r == null)
r = m.get(toLowerCase(section));
if (r == null)
return Collections.emptySet();
return Collections.unmodifiableSet(r);
}
Set<String> getNames(String section, String subsection) {
List<ConfigLine> s = sorted();
int idx = find(s, section, subsection, "");
if (idx < 0)
idx = -(idx + 1);
Map<String, String> m = new LinkedHashMap<String, String>();
while (idx < s.size()) {
ConfigLine e = s.get(idx++);
if (!e.match(section, subsection))
break;
if (e.name == null)
continue;
String l = toLowerCase(e.name);
if (!m.containsKey(l))
m.put(l, e.name);
}
return new CaseFoldingSet(m);
}
String[] get(String section, String subsection, String name) { String[] get(String section, String subsection, String name) {
List<ConfigLine> s = sorted(); List<ConfigLine> s = sorted();
int idx = find(s, section, subsection, name); int idx = find(s, section, subsection, name);
@ -167,4 +211,86 @@ class ConfigSnapshot {
b.section, b.subsection, b.name); b.section, b.subsection, b.name);
} }
} }
private SectionNames names() {
SectionNames n = names;
if (n == null)
names = n = new SectionNames(this);
return n;
}
private static class SectionNames {
final CaseFoldingSet sections;
final Map<String, Set<String>> subsections;
SectionNames(ConfigSnapshot cfg) {
Map<String, String> sec = new LinkedHashMap<String, String>();
Map<String, Set<String>> sub = new HashMap<String, Set<String>>();
while (cfg != null) {
for (ConfigLine e : cfg.entryList) {
if (e.section == null)
continue;
String l1 = toLowerCase(e.section);
if (!sec.containsKey(l1))
sec.put(l1, e.section);
if (e.subsection == null)
continue;
Set<String> m = sub.get(l1);
if (m == null) {
m = new LinkedHashSet<String>();
sub.put(l1, m);
}
m.add(e.subsection);
}
cfg = cfg.baseState;
}
sections = new CaseFoldingSet(sec);
subsections = sub;
}
}
private static class CaseFoldingSet extends AbstractSet<String> {
private final Map<String, String> names;
CaseFoldingSet(Map<String, String> names) {
this.names = names;
}
@Override
public boolean contains(Object needle) {
if (needle instanceof String) {
String n = (String) needle;
return names.containsKey(n)
|| names.containsKey(StringUtils.toLowerCase(n));
}
return false;
}
@Override
public Iterator<String> iterator() {
final Iterator<String> i = names.values().iterator();
return new Iterator<String>() {
public boolean hasNext() {
return i.hasNext();
}
public String next() {
return i.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return names.size();
}
}
} }

Loading…
Cancel
Save