Ticket #11382: mapcss-use-bitmap-and-keycache-size.patch

File mapcss-use-bitmap-and-keycache-size.patch, 12.4 KB (added by anonymous, 9 years ago)
  • src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java

    diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
    index 6de6336..80f8449 100644
    a b import java.lang.reflect.Field;  
    1212import java.nio.charset.StandardCharsets;
    1313import java.text.MessageFormat;
    1414import java.util.ArrayList;
    15 import java.util.BitSet;
    1615import java.util.Collections;
    1716import java.util.HashMap;
    1817import java.util.HashSet;
    public class MapCSSStyleSource extends StyleSource {  
    138137     */
    139138    public static class MapCSSRuleIndex {
    140139        /**
    141          * This is an iterator over all rules that are marked as possible in the bitset.
     140         * This is a subset of the MapCSS rules. It is represented by storing the indexes in the rules array. This allows for fast merging.
     141         *
     142         * It alternatively uses a list of set indexes, to ensure O(<number of set bits>) space requirement.
     143         *
     144         * @author Michael Zangl
     145         */
     146        private final static class RuleBitset {
     147            /**
     148             * A (sorted) array of rule indexes. Those indexes are the allowed rules. We do not use an {@link ArrayList} here because this would require us to box all integers (slow).
     149             */
     150            private int[] ruleIndexes = new int[5];
     151
     152            /**
     153             * Real length of {@link #ruleIndexes}
     154             */
     155            private int ruleIndexesLength = 0;
     156
     157            /**
     158             * The bitset data. We always store whole words. If it is <code>null</code>, this is not used.
     159             */
     160            private long[] bitsetData;
     161            /**
     162             * The number of words after which bitsetData starts.
     163             */
     164            private int bitsetOffset;
     165
     166            private void addRule(int ruleIndex) {
     167                if (bitsetData != null) {
     168                    throw new IllegalStateException("Cannot add to optimized bitset.");
     169                }
     170                if (ruleIndexesLength > 0 && ruleIndexes[ruleIndexesLength - 1] >= ruleIndex) {
     171                    throw new IllegalArgumentException("Rules must be sorted.");
     172                }
     173
     174                if (ruleIndexesLength >= ruleIndexes.length) {
     175                    int[] newIndexes = new int[ruleIndexes.length * 2];
     176                    System.arraycopy(ruleIndexes, 0, newIndexes, 0, ruleIndexes.length);
     177                    ruleIndexes = newIndexes;
     178                }
     179
     180                ruleIndexes[ruleIndexesLength] = ruleIndex;
     181                ruleIndexesLength++;
     182            }
     183
     184            /**
     185             * Optimize this list by converting it to a bitset (at least partially)
     186             */
     187            private void optimize() {
     188                if (ruleIndexesLength == 0) {
     189                    return;
     190                }
     191
     192                int minRule = ruleIndexes[0];
     193                int maxRule = ruleIndexes[ruleIndexesLength - 1];
     194
     195                // current policy: completely convert to bitset if on average 1 bit per word is set.
     196                // alternatives: convert one dense area, ...
     197                int usedWords = getWord(maxRule) - getWord(minRule) + 1;
     198                if (usedWords <= ruleIndexesLength - 2) {
     199                    bitsetOffset = getWord(minRule);
     200                    bitsetData = new long[usedWords];
     201                    for (int i = 0; i < ruleIndexesLength; i++) {
     202                        int ruleIndex = ruleIndexes[i];
     203                        bitsetData[getWord(ruleIndex) - bitsetOffset] |= getMask(ruleIndex);
     204                    }
     205                    ruleIndexes = null;
     206                    ruleIndexesLength = 0;
     207                }
     208            }
     209
     210            private static long getMask(int bit) {
     211                return (1l << (bit & 63));
     212            }
     213
     214            private static int getWord(int bit) {
     215                return bit / 64;
     216            }
     217        }
     218
     219        /**
     220         * This is an iterator over all rules that are marked as possible in the bitsets. Rules can be added to it before it is used.
    142221         *
    143222         * @author Michael Zangl
    144223         */
    145224        private final class RuleCandidatesIterator implements Iterator<MapCSSRule> {
    146             private final BitSet ruleCandidates;
     225            /**
     226             * The rules that this iterator should iterate over.
     227             * <p>
     228             * We cannot use a bitset here, because it does not expose it's long[]
     229             */
     230            private final long[] ruleCandidateBits;
    147231            private int next;
    148232
    149             private RuleCandidatesIterator(BitSet ruleCandidates) {
    150                 this.ruleCandidates = ruleCandidates;
    151                 next = ruleCandidates.nextSetBit(0);
     233            private RuleCandidatesIterator() {
     234                ruleCandidateBits = new long[RuleBitset.getWord(rules.size() - 1) + 1];
     235            }
     236
     237            private int nextSetBit(int from) {
     238                int wordIndex = RuleBitset.getWord(from);
     239                if (wordIndex == ruleCandidateBits.length) {
     240                    return -1;
     241                }
     242
     243                long nextWord = ruleCandidateBits[wordIndex] & (-1l << from);
     244
     245                while (true) {
     246                    if (nextWord != 0)
     247                        return (wordIndex * 64) + Long.numberOfTrailingZeros(nextWord);
     248
     249                    wordIndex++;
     250                    if (wordIndex == ruleCandidateBits.length)
     251                        return -1;
     252                    nextWord = ruleCandidateBits[wordIndex];
     253                }
     254            }
     255
     256            public void or(RuleBitset set) {
     257                if (set.bitsetData != null) {
     258                    for (int i = 0 ; i < set.bitsetData.length; i++) {
     259                        ruleCandidateBits[set.bitsetOffset + i] |= set.bitsetData[i] ;
     260                    }
     261                }
     262
     263                for (int i = 0; i < set.ruleIndexesLength; i++) {
     264                    setBit(set.ruleIndexes[i]);
     265                }
     266            }
     267
     268            private void setBit(int bitIndex) {
     269                ruleCandidateBits[RuleBitset.getWord(bitIndex)] |= RuleBitset.getMask(bitIndex);
     270            }
     271
     272            public void prepare() {
     273                next = nextSetBit(0);
    152274            }
    153275
    154276            @Override
    public class MapCSSStyleSource extends StyleSource {  
    159281            @Override
    160282            public MapCSSRule next() {
    161283                MapCSSRule rule = rules.get(next);
    162                 next = ruleCandidates.nextSetBit(next + 1);
     284                next = nextSetBit(next + 1);
    163285                return rule;
    164286            }
    165287
    public class MapCSSStyleSource extends StyleSource {  
    167289            public void remove() {
    168290                throw new UnsupportedOperationException();
    169291            }
     292
     293            @Override
     294            public String toString() {
     295                StringBuilder builder = new StringBuilder();
     296                builder.append("RuleCandidatesIterator [ruleCandidateBits=[");
     297                boolean needComma = false;
     298                for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) {
     299                    if (needComma) {
     300                        builder.append(", ");
     301                    }
     302                    needComma = true;
     303                    builder.append(i);
     304                }
     305                builder.append("], next=");
     306                builder.append(next);
     307                builder.append("]");
     308                return builder.toString();
     309            }
     310
     311
    170312        }
    171313
    172314        /**
    public class MapCSSStyleSource extends StyleSource {  
    178320            /**
    179321             * The indexes of rules that might be applied if this tag is present and the value has no special handling.
    180322             */
    181             BitSet generalRules = new BitSet();
     323            RuleBitset generalRules = new RuleBitset();
    182324
    183325            /**
    184326             * A map that sores the indexes of rules that might be applied if the key=value pair is present on this
    185327             * primitive. This includes all key=* rules.
    186328             */
    187             Map<String, BitSet> specialRules = new HashMap<>();
     329            HashMap<String, RuleBitset> specialRules = new HashMap<>();
    188330
    189331            public void addForKey(int ruleIndex) {
    190                 generalRules.set(ruleIndex);
    191                 for (BitSet r : specialRules.values()) {
    192                     r.set(ruleIndex);
    193                 }
     332                generalRules.addRule(ruleIndex);
    194333            }
    195334
    196335            public void addForKeyAndValue(String value, int ruleIndex) {
    197                 BitSet forValue = specialRules.get(value);
     336                RuleBitset forValue = specialRules.get(value);
    198337                if (forValue == null) {
    199                     forValue = new BitSet();
    200                     forValue.or(generalRules);
     338                    forValue = new RuleBitset();
    201339                    specialRules.put(value.intern(), forValue);
    202340                }
    203                 forValue.set(ruleIndex);
     341                forValue.addRule(ruleIndex);
     342            }
     343
     344            public void get(RuleCandidatesIterator i, String value) {
     345                RuleBitset forValue = specialRules.get(value);
     346                if (forValue != null)
     347                    i.or(forValue);
     348                i.or(generalRules);
    204349            }
    205350
    206             public BitSet get(String value) {
    207                 BitSet forValue = specialRules.get(value);
    208                 if (forValue != null) return forValue; else return generalRules;
     351            public void optimize() {
     352                generalRules.optimize();
     353                for (RuleBitset bitset : specialRules.values()) {
     354                    bitset.optimize();
     355                }
    209356            }
    210357        }
    211358
    public class MapCSSStyleSource extends StyleSource {  
    220367        /**
    221368         * Rules that do not require any key to be present. Only the index in the {@link #rules} array is stored.
    222369         */
    223         private final BitSet remaining = new BitSet();
     370        private RuleBitset remaining = new RuleBitset();
    224371
    225372        /**
    226373         * Add a rule to this index. This needs to be called before {@link #initIndex()} is called.
    public class MapCSSStyleSource extends StyleSource {  
    246393                }
    247394                OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost;
    248395                if (s.conds == null) {
    249                     remaining.set(ruleIndex);
     396                    remaining.addRule(ruleIndex);
    250397                    continue;
    251398                }
    252399                List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds,
    public class MapCSSStyleSource extends StyleSource {  
    259406                    if (key != null) {
    260407                        getEntryInIndex(key).addForKey(ruleIndex);
    261408                    } else {
    262                         remaining.set(ruleIndex);
     409                        remaining.addRule(ruleIndex);
    263410                    }
    264411                }
    265412            }
     413
     414            remaining.optimize();
     415            for (MapCSSKeyRules rules : index.values()) {
     416                rules.optimize();
     417            }
    266418        }
    267419
    268420        /**
    public class MapCSSStyleSource extends StyleSource {  
    312464         * @return An iterator over possible rules in the right order.
    313465         */
    314466        public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) {
    315             final BitSet ruleCandidates = new BitSet(rules.size());
     467            final RuleCandidatesIterator ruleCandidates = new RuleCandidatesIterator();
    316468            ruleCandidates.or(remaining);
    317469
    318470            for (Map.Entry<String, String> e : osm.getKeys().entrySet()) {
    319471                MapCSSKeyRules v = index.get(e.getKey());
    320472                if (v != null) {
    321                     BitSet rs = v.get(e.getValue());
    322                     ruleCandidates.or(rs);
     473                    v.get(ruleCandidates, e.getValue());
    323474                }
    324475            }
    325             return new RuleCandidatesIterator(ruleCandidates);
     476            ruleCandidates.prepare();
     477            return ruleCandidates;
    326478        }
    327479
    328480        /**
    public class MapCSSStyleSource extends StyleSource {  
    333485        public void clear() {
    334486            rules.clear();
    335487            index.clear();
    336             remaining.clear();
     488            remaining = new RuleBitset();
    337489        }
    338490    }
    339491