All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cassandra.repair.asymmetric.RangeMap Maven / Gradle / Ivy

Go to download

The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.

There is a newer version: 5.0-rc1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.cassandra.repair.asymmetric;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.AbstractIterator;

import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;

public class RangeMap implements Map, T>
{
    private static final Comparator> comparator = Comparator.comparing((Range o) -> o.left);

    private final NavigableMap, T> byStart;

    public RangeMap()
    {
        byStart = new TreeMap<>(comparator);
    }

    public int size()
    {
        return byStart.size();
    }

    public boolean isEmpty()
    {
        return byStart.isEmpty();
    }

    public boolean containsKey(Object key)
    {
        return byStart.containsKey(key);
    }

    public boolean containsValue(Object value)
    {
        return byStart.containsValue(value);
    }

    public T get(Object key)
    {
        return byStart.get(key);
    }

    public T put(Range key, T value)
    {
        assertNonIntersecting(key);
        return byStart.put(key, value);
    }

    private void assertNonIntersecting(Range range)
    {
        // todo: wraparound
        Range before = byStart.floorKey(range);
        Range after = byStart.ceilingKey(range);
        assert before == null || !before.intersects(range);
        assert after == null || !after.intersects(range);
    }

    public T remove(Object key)
    {
        return byStart.remove(key);
    }

    public void putAll(Map, ? extends T> m)
    {
        byStart.putAll(m);
    }

    public void clear()
    {
        byStart.clear();
    }

    public Set> keySet()
    {
        return byStart.keySet();
    }

    public Collection values()
    {
        return byStart.values();
    }

    public Set, T>> entrySet()
    {
        return byStart.entrySet();
    }

    /**
     * might return duplicate entries if range.isWrapAround()
     *
     * don't depend on the order of the entries returned
     */
    @VisibleForTesting
    Iterator, T>> intersectingEntryIterator(Range range)
    {
        return range.isWrapAround() ? new WrappingIntersectingIterator(range) : new IntersectingIterator(range);
    }

    public Set, T>> removeIntersecting(Range range)
    {
        Iterator, T>> iter = intersectingEntryIterator(range);
        Set, T>> intersecting = new HashSet<>();
        while (iter.hasNext())
        {
            Map.Entry, T> entry = iter.next();
            intersecting.add(entry);
        }
        for (Map.Entry, T> entry : intersecting)
            byStart.remove(entry.getKey());
        return intersecting;
    }

    private class WrappingIntersectingIterator extends AbstractIterator, T>>
    {
        private final Iterator, T>>> iterators;
        private Iterator, T>> currentIter;

        public WrappingIntersectingIterator(Range range)
        {
            List, T>>> iters = new ArrayList<>(2);
            for (Range unwrapped : range.unwrap())
                iters.add((new IntersectingIterator(unwrapped)));
            iterators = iters.iterator();
        }
        protected Map.Entry, T> computeNext()
        {
            if ((currentIter == null || !currentIter.hasNext()) && iterators.hasNext())
                currentIter = iterators.next();
            if (currentIter != null && currentIter.hasNext())
                return currentIter.next();
            return endOfData();
        }
    }

    private class IntersectingIterator extends AbstractIterator, T>>
    {
        private final Iterator, T>> tailIterator;
        private final Range range;
        // since we guarantee no ranges overlap in byStart, we know the last entry is possibly the wrap around range
        private boolean shouldReturnLast = false;

        public IntersectingIterator(Range range)
        {
            Range startKey = byStart.floorKey(range);
            tailIterator = startKey == null ? byStart.entrySet().iterator() :
                                              byStart.tailMap(startKey, true).entrySet().iterator();
            Range last = byStart.isEmpty() ? null : byStart.lastKey();
            if (last != null && last.isWrapAround() && last.intersects(range))
                shouldReturnLast = true;
            this.range = range;
        }

        protected Map.Entry, T> computeNext()
        {
            if (shouldReturnLast)
            {
                shouldReturnLast = false;
                return new Entry<>(byStart.lastEntry());
            }
            while (tailIterator.hasNext())
            {
                Entry, T> candidateNext = new Entry<>(tailIterator.next());
                Range candidateRange = candidateNext.getKey();

                if (candidateRange.isWrapAround()) // we know we already returned any wrapping range
                    continue;

                if (candidateRange.left.compareTo(range.right) >= 0 && (!range.isWrapAround())) // range is unwrapped, but that means one range has right == min token and is still wrapping
                    return endOfData();

                if (range.left.compareTo(candidateRange.right) >= 0)
                    continue;

                return candidateNext;
            }
            return endOfData();
        }
    }

    public String toString()
    {
        return byStart.toString();
    }

    static class Entry implements Map.Entry
    {
        private final V v;
        private final K k;

        Entry(K key, V val)
        {
            this.k = key;
            this.v = val;
        }

        Entry(Map.Entry toClone)
        {
            this(toClone.getKey(), toClone.getValue());
        }
        public K getKey()
        {
            return k;
        }

        public V getValue()
        {
            return v;
        }

        public V setValue(V value)
        {
            throw new UnsupportedOperationException();
        }

        public boolean equals(Object o)
        {
            if (this == o) return true;
            if (!(o instanceof Map.Entry)) return false;
            Map.Entry entry = (Map.Entry) o;
            return v.equals(entry.getValue()) &&
                   k.equals(entry.getKey());
        }

        public int hashCode()
        {
            return Objects.hash(v, k);
        }

        public String toString()
        {
            return "Entry{" +
                   "v=" + v +
                   ", k=" + k +
                   '}';
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy