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
tags/v2.0.0.201206130900-r
Shawn O. Pearce 12 years ago
parent
commit
c0b1443926

+ 1
- 1
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java View File

@@ -414,9 +414,9 @@ public class ConfigTest {
names.contains("repositoryformatversion"));

Iterator<String> itr = names.iterator();
assertEquals("repositoryFormatVersion", itr.next());
assertEquals("filemode", itr.next());
assertEquals("logAllRefUpdates", itr.next());
assertEquals("repositoryFormatVersion", itr.next());
assertFalse(itr.hasNext());
}


+ 10
- 148
org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java View File

@@ -52,14 +52,9 @@
package org.eclipse.jgit.lib;

import java.text.MessageFormat;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

@@ -479,17 +474,22 @@ public class Config {
* section to search for.
* @return set of all subsections of specified section within this
* 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) {
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() {
return get(new SectionNames());
return getState().getSections();
}

/**
@@ -509,7 +509,7 @@ public class Config {
* @return the list of names defined for this 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);
}

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 final char[] buf;


+ 126
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java View File

@@ -52,19 +52,29 @@ package org.eclipse.jgit.lib;

import static org.eclipse.jgit.util.StringUtils.compareIgnoreCase;
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.Collections;
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.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jgit.util.StringUtils;

class ConfigSnapshot {
final List<ConfigLine> entryList;
final Map<Object, Object> cache;
final ConfigSnapshot baseState;
volatile List<ConfigLine> sorted;
volatile SectionNames names;

ConfigSnapshot(List<ConfigLine> entries, ConfigSnapshot base) {
entryList = entries;
@@ -72,6 +82,40 @@ class ConfigSnapshot {
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) {
List<ConfigLine> s = sorted();
int idx = find(s, section, subsection, name);
@@ -167,4 +211,86 @@ class ConfigSnapshot {
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