/** * 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 com.facebook.infrastructure.dht; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.facebook.infrastructure.gms.GossipDigest; import com.facebook.infrastructure.io.ICompactSerializer; import com.facebook.infrastructure.io.IFileReader; import com.facebook.infrastructure.io.IFileWriter; import com.facebook.infrastructure.net.CompactEndPointSerializationHelper; import com.facebook.infrastructure.net.EndPoint; import com.facebook.infrastructure.service.StorageService; /** * A representation of the range that a node is responsible for on the DHT ring. * Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com ) */ public class Range implements Comparable<Range> { private static ICompactSerializer<Range> serializer_; static { serializer_ = new RangeSerializer(); } public static ICompactSerializer<Range> serializer() { return serializer_; } public static boolean isKeyInRanges(List<Range> ranges, String key) { if(ranges == null ) return false; for ( Range range : ranges) { if(range.contains(StorageService.hash(key))) { return true ; } } return false; } private BigInteger left_; private BigInteger right_; public Range(BigInteger left, BigInteger right) { left_ = left; right_ = right; } /** * Returns the left endpoint of a range. * @return left endpoint */ public BigInteger left() { return left_; } /** * Returns the right endpoint of a range. * @return right endpoint */ public BigInteger right() { return right_; } boolean isSplitRequired() { return ( left_.subtract(right_).signum() >= 0 ); } public boolean isSplitBy(BigInteger bi) { if ( left_.subtract(right_).signum() > 0 ) { /* * left is greater than right we are wrapping around. * So if the interval is [a,b) where a > b then we have * 3 cases one of which holds for any given token k. * (1) k > a -- return true * (2) k < b -- return true * (3) b < k < a -- return false */ if ( bi.subtract(left_).signum() > 0 ) return true; else if (right_.subtract(bi).signum() > 0 ) return true; else return false; } else if ( left_.subtract(right_).signum() < 0 ) { /* * This is the range [a, b) where a < b. */ return ( bi.subtract(left_).signum() > 0 && right_.subtract(bi).signum() > 0 ); } else { // should never be here. return true; } } /** * Helps determine if a given point on the DHT ring is contained * in the range in question. * @param bi point in question * @return true if the point contains within the range else false. */ public boolean contains(BigInteger bi) { if ( left_.subtract(right_).signum() > 0 ) { /* * left is greater than right we are wrapping around. * So if the interval is [a,b) where a > b then we have * 3 cases one of which holds for any given token k. * (1) k > a -- return true * (2) k < b -- return true * (3) b < k < a -- return false */ if ( bi.subtract(left_).signum() >= 0 ) return true; else if (right_.subtract(bi).signum() > 0 ) return true; else return false; } else if ( left_.subtract(right_).signum() < 0 ) { /* * This is the range [a, b) where a < b. */ return ( bi.subtract(left_).signum() >= 0 && right_.subtract(bi).signum() >=0 ); } else { return true; } } /** * Helps determine if a given range on the DHT ring is contained * within the range associated with the <i>this</i> pointer. * @param rhs rhs in question * @return true if the point contains within the range else false. */ public boolean contains(Range rhs) { /* * If (a, b] and (c, d} are not wrap arounds * then return true if a <= c <= d <= b. */ if ( !isWrapAround(this) && !isWrapAround(rhs) ) { if ( rhs.left_.subtract(left_).signum() >= 0 && right_.subtract(rhs.right_).signum() >= 0 ) return true; else return false; } /* * If lhs is a wrap around and rhs is not then * rhs.left >= lhs.left and rhs.right >= lhs.left. */ if ( isWrapAround(this) && !isWrapAround(rhs) ) { if ( rhs.left_.subtract(left_).signum() >= 0 && rhs.right_.subtract(right_).signum() >= 0 ) return true; else return false; } /* * If lhs is not a wrap around and rhs is a wrap * around then we just return false. */ if ( !isWrapAround(this) && isWrapAround(rhs) ) return false; if( isWrapAround(this) && isWrapAround(rhs) ) { if ( rhs.left_.subtract(left_).signum() >= 0 && right_.subtract(right_).signum() >= 0 ) return true; else return false; } /* should never be here */ return false; } /** * Tells if the given range is a wrap around. * @param range * @return */ private boolean isWrapAround(Range range) { boolean bVal = ( range.left_.subtract(range.right_).signum() > 0 ) ? true : false; return bVal; } public int compareTo(Range rhs) { /* * If the range represented by the "this" pointer * is a wrap around then it is the smaller one. */ if ( isWrapAround(this) ) return -1; if ( isWrapAround(rhs) ) return 1; return right_.compareTo(rhs.right_); } public boolean equals(Object o) { if ( !(o instanceof Range) ) return false; Range rhs = (Range)o; if ( left_.equals(rhs.left_) && right_.equals(rhs.right_) ) return true; else return false; } public int hashCode() { return toString().hashCode(); } public String toString() { return "(" + left_ + "," + right_ + "]"; } } class RangeSerializer implements ICompactSerializer<Range> { public void serialize(Range range, DataOutputStream dos) throws IOException { dos.writeUTF(range.left().toString()); dos.writeUTF(range.right().toString()); } public Range deserialize(DataInputStream dis) throws IOException { BigInteger left = new BigInteger(dis.readUTF()); BigInteger right = new BigInteger(dis.readUTF()); return new Range(left, right); } }