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
This commit is contained in:
parent
2ba67bedae
commit
c0b1443926
|
@ -414,9 +414,9 @@ public void test009_readNamesInSection() throws ConfigInvalidException {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 String[] getStringList(final String section, String subsection,
|
||||||
* 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 Set<String> getNames(String section) {
|
||||||
* @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 static interface SectionParser<T> {
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -52,19 +52,29 @@
|
||||||
|
|
||||||
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 @@ public int compare(ConfigLine a, ConfigLine b) {
|
||||||
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…
Reference in New Issue