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

com.fasterxml.clustermate.api.KeyRange Maven / Gradle / Ivy

Go to download

Data types needed for service abstraction, used by both server and client components.

The newest version!
package com.fasterxml.clustermate.api;

import java.util.BitSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fasterxml.clustermate.api.KeyHash;
import com.fasterxml.clustermate.api.KeyRange;
import com.fasterxml.clustermate.api.KeySpace;

/**
 * Logical slice of a {@link KeySpace}; a consecutive range of individual
 * key values within value space, defined by two parameters:
 * starting key value (inclusive), and length between 0 and length of
 * {@link KeySpace}.
 *

* Instances have natural ordering based on {@link #getStart()}, ascending. */ public class KeyRange implements Comparable { // [0,+60] protected final static Pattern DESC_PATTERN = Pattern.compile("\\[(\\d+),\\s*\\+(\\d+)\\]"); /** * Key space this range is contained in. */ protected final KeySpace _keyspace; /** * Local copy of the total length of space (to avoid lookups * from parent); used for figuring out where wrapping * should occur */ protected final int _spaceEnd; protected final int _start; protected final int _length; /* /********************************************************************** /* Construction, factory methods /********************************************************************** */ protected KeyRange(//@JsonProperty("keyspace") KeySpace space, //@JsonProperty("start") int start, //@JsonProperty("length") int length) { _keyspace = space; _spaceEnd = space.getLength(); _start = start; _length = length; } // Constructor used by deserializer protected KeyRange(External ext) { _keyspace = ext.keyspace; _spaceEnd = _keyspace.getLength(); _start = ext.start; _length = ext.length; } public static KeyRange valueOf(KeySpace space, String ref) throws IllegalArgumentException { // let's accept null, empty String as empty range if (ref == null) { return space.emptyRange(); } ref = ref.trim(); if (ref.isEmpty()) { return space.emptyRange(); } // otherwise must parse try { Matcher m = DESC_PATTERN.matcher(ref); if (m.matches()) { int start = Integer.parseInt(m.group(1)); int len = Integer.parseInt(m.group(2)); return new KeyRange(space, start, len); } } catch (IllegalArgumentException e) { } throw new IllegalArgumentException("Invalid KeyRange reference '"+ref+"'"); } /* /********************************************************************** /* Fluent factories (mutant factories) /********************************************************************** */ /** * Fluent factory method for creating a range with same start * point, but possibly different length */ public KeyRange withLength(int newLength) throws IllegalArgumentException { if (newLength == _length) { return this; } if (newLength < 0 || newLength > _spaceEnd) { throw new IllegalArgumentException("Illegal 'newLength' ("+newLength+"): must be [0, " +_spaceEnd+"]"); } return new KeyRange(_keyspace, _start, newLength); } /** * Method for calculating overlap range between this range and * given range; that is, range that is covered by both ranges. * If there is no overlap, an empty Range (with * start of 0, length of 0) will be returned. */ public KeyRange intersection(KeyRange other) { return intersection(this, other); } /** * Method for calculating overlap between given ranges; that is, a range * that is covered by both ranges. If there is no overlap, an empty Range (with * start of 0, length of 0) will be returned. */ public static KeyRange intersection(KeyRange range1, KeyRange range2) { /* First: one special case, which is rather difficult to handle * using general method -- that of "full ranges". Easiest thing * to do is to simply return "the other range" if either one is * full. */ if (range1._coversWholeKeyspace()) { return range2; } if (range2._coversWholeKeyspace()) { return range1; } // Ok, let's see if range2 starts within range1: if (range1._containsPoint(range2.getStart())) { return _intersect(range1, range2); } if (range2._containsPoint(range1.getStart())) { return _intersect(range2, range1); } return range1._keyspace.emptyRange(); } // Helper method called when we know range2 starst within range1 private final static KeyRange _intersect(KeyRange range1, KeyRange range2) { // potential length of range1 needs to be subtracted by 'leading' part of range2 int len = range1.getLength() - range1._clockwiseDistance(range2.getStart()); len = Math.min(len, range2.getLength()); return range2.withLength(len); } /** * Method for calculating combined range formed by this range * and given range; ranges must overlap or be adjacent (if they * are not, {@link IllegalArgumentException} will be thrown) */ public KeyRange union(KeyRange other) { return union(this, other); } /** * Method for calculating combined range formed by two specified * overlapping or adjacent ranges * Ranges must overlap or be adjacent: if they * don't, {@link IllegalArgumentException} will be thrown; * that is, this method does not work for disjoint ranges. */ public static KeyRange union(KeyRange range1, KeyRange range2) { // First two special cases we can weed out if (range1._coversWholeKeyspace()) { return range1; } if (range2._coversWholeKeyspace()) { return range1; } final int start1 = range1.getStart(); final int start2 = range2.getStart(); // Ok, let's see if range2 starts within or next to range1: if (range1._containsOrBordersPoint(start2)) { // if so, see how far it extends int newLen = range1._clockwiseDistance(start2) + range2.getLength(); if (newLen < range1.getLength()) { return range1; } // note: can't exceed max length (could if union "overlaps with itself") return range1.withLength(Math.min(newLen, range1._spaceEnd)); } if (range2._containsOrBordersPoint(start1)) { int newLen = range2._clockwiseDistance(start1) + range1.getLength(); if (newLen < range2.getLength()) { return range2; } return range2.withLength(Math.min(newLen, range2._spaceEnd)); } throw new IllegalArgumentException("Ranges "+range1+" and "+range2+" do not overlap or be adjacent"); } /* /********************************************************************** /* Simple accessors /********************************************************************** */ public KeySpace getKeyspace() { return _keyspace; } public int getStart() { return _start; } public int getLength() { return _length; } public void fill(BitSet bits) { if (wrapped()) { bits.set(_start, _spaceEnd); bits.set(0, (_start+_length) - _spaceEnd); } else { bits.set(_start, _start + _length); } } /** * Method used for checking whether given hash key is contained * in this range */ public boolean contains(KeyHash key) { return _containsPoint(key.getModuloHash()); } /** * Alter lookup method used in cases where caller wants to minimize * allocations of {@link KeyHash} instances. * NOTE: rarely needed; only used by certain bulk operations. */ public boolean contains(int rawHash) { int mod = KeyHash.calcModulo(rawHash, _spaceEnd); return _containsPoint(mod); } /** * Method used for checking whether given key range is fully contained * in this range or not. */ public boolean contains(KeyRange other) { // one way to solve is to first handle non-wrapped-around case: int thisEnd = _linearEnd(); int thatEnd = other._linearEnd(); if ((other._start >= _start) && (thatEnd <= thisEnd)) { return true; } // then: wrap-around case, bit more complex if (thisEnd >= _spaceEnd) { // either way, can not contain something that's bigger than this one if (other._length > _length) { return false; } // offset space to the "other end" then thisEnd -= _spaceEnd; int thisStart = thisEnd - _length; if ((other._start >= thisStart) && (thatEnd <= thisEnd)) { return true; } } return false; } /** * Method for checking whether given range overlaps with this * range. */ public boolean overlapsWith(KeyRange other) { return rangesOverlap(this, other); } /** * Method for checking whether given ranges overlap */ public static boolean rangesOverlap(KeyRange range1, KeyRange range2) { return range1._containsPoint(range2.getStart()) || range2._containsPoint(range1.getStart()); } /** * Method that calculates clock-wise distance given key has, from * beginning of this range. This is typically used to find the * "closest" range for given key. *
* NOTE: method does NOT check whether this range actually contains * key (i.e. that {@link #contains} would return true); it merely calculates * distance from the start assuming range was large enough. * If you want to verify containment, you may want to instead call * {@link #clockwiseDistanceIfContained(KeyHash, int)}. */ public int clockwiseDistance(KeyHash key) { return _clockwiseDistance(key.getModuloHash()); } public int clockwiseDistance(KeyRange otherRange) { return _clockwiseDistance(otherRange.getStart()); } public int clockwiseDistanceIfContained(KeyHash key, int notContainedMarker) { int dist = _clockwiseDistance(key.getModuloHash()); if (dist < _length) { // yes, is contained return dist; } // nope: return marker return notContainedMarker; } /** * Convenience method that is equivalent of: *

     *   getLength() == 0
     *
*/ public boolean empty() { return _length == 0; } /** * Method that can be used to check whether this range "wraps" * around end of linear range. * * @return True if range extends past logical end and wraps so that * it covers both "end" and "start" of linear key space */ public boolean wrapped() { return (_start + _length) > _spaceEnd; } /* /********************************************************************** /* Overrides /********************************************************************** */ @Override public boolean equals(Object o) { if (o == this) return true; if (o == null) return false; if (o.getClass() != getClass()) return false; KeyRange other = (KeyRange) o; // should we compare key space directly? No, should be enough to verify space length: return (other._start == _start) && (other._length == _length) && (other._spaceEnd == _spaceEnd); } @Override public int hashCode() { return _length ^ _start; } @Override public String toString() { return "["+_start+",+"+_length+"]"; } @Override public int compareTo(KeyRange other) { int otherStart = other._start; if (_start < otherStart) return -1; return (_start == otherStart) ? 0 : 1; } /* /********************************************************************** /* Internal methods /********************************************************************** */ protected int _clockwiseDistance(int point) { int dist = point - _start; if (dist < 0) { // wrap-around case dist += _spaceEnd; } return dist; } protected boolean _containsPoint(int point) { int linearEnd = _start + _length; if (point >= _start) { return (point < linearEnd); } // if range is wrapped, may still contain in wrapped beginning int wrappedEnd = linearEnd - _spaceEnd; return (point < wrappedEnd); } protected boolean _containsOrBordersPoint(int point) { int linearEnd = _start + _length; if (point >= _start) { return (point <= linearEnd); } // if range is wrapped, may still contain in wrapped beginning int wrappedEnd = linearEnd - _spaceEnd; return (point <= wrappedEnd); } protected int _linearEnd() { return _start + _length; } protected boolean _coversWholeKeyspace() { return _length == _spaceEnd; } /* /********************************************************************** /* Helper types for (de)serialization /********************************************************************** */ /** * Helper class used for deserialization, used as "delegating" * class */ public static class External { public KeySpace keyspace; public int start; public int length; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy