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: I7b3269565b1308f69d20dc3f3fe917aea00f8a73tags/v2.0.0.201206130900-r
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()); | ||||
} | } | ||||
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; | ||||
* 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(); | |||||
} | } | ||||
/** | /** | ||||
* @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); | |||||
} | } | ||||
/** | /** | ||||
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; | ||||
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; | ||||
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); | ||||
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(); | |||||
} | |||||
} | |||||
} | } |