// Commented for the Learning branch package com.limegroup.gnutella.downloader; import java.io.Serializable; import com.limegroup.gnutella.ByteOrder; /** * An Interval object clips out a range within a file. * * A file composed of bytes of data looks like this: * * bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb * iiiiiiiiiiiii * low -------> * high -------------------> * * An Interval object contains two indices, named low and high. * Together, they clip out a portion of the file, describing a specific interval. * low and high are measured in bytes from the start of the file. * * low and high are inclusive on both ends. * low points to the start of the interval. * high doesn't point to the end of the interval, rather, it points to the last byte in the interval. * * low has to be less than or equal to high. * if low equals high, the interval is the single byte both point to. */ public class Interval implements Serializable { /** * A long unique number that can identify an Interval object that has been written to a file on the disk. * Change this number when Interval changes, not breaking compatibility. */ static final long serialVersionUID = -2562093104400487554L; /* * Always, low has to be less than or equal to high. */ /** * The index of the start of the interval of data in the file. * * low is the distance in bytes from the start of the file to the start of the interval. * The first byte in the file has the index 0. */ public final int low; /** * The index of the end of the interval of data in the file. * The interval includes the byte high points to. * * high is the distance in bytes from the start of the file to the last byte of the interval. * The first byte in the file has the index 0. */ public final int high; /** * Make a new Interval object that clips out a range of data in a file. * * low must be less than or equal to high. * low and high are passed as long numbers, but have to be small enough to fit into int numbers. * * @param low The distance in bytes from the start of the file to the start of the interval * @param high The distance in bytes from the start of the file to the last byte of the interval */ public Interval(long low, long high) { // Make sure low is less than or equal to high if (high < low) throw new IllegalArgumentException("low: " + low + ", high: " + high); // Make sure high and low are small enough to fit into int variables if (low < 0) throw new IllegalArgumentException("low < min int:" + low); if (high > Integer.MAX_VALUE) throw new IllegalArgumentException("high > max int:" + high); // Save the given indices this.low = (int)low; // Cast them to int, we made sure they're small enough this.high = (int)high; } /** * Make a new Interval object that clips out a single byte in a file. * the given index is a long, but must be small enough to fit safely in an int. * * @param singleton The index of the byte that will be the whole interval */ public Interval(long singleton) { // Make sure the given index is small enough to fit in an int if (singleton < Integer.MIN_VALUE) throw new IllegalArgumentException("singleton < min:" + singleton); if (singleton > Integer.MAX_VALUE) throw new IllegalArgumentException("singleton > max int:" + singleton); // Save the given index as low and high, this clips out a single byte this.low = (int)singleton; this.high = (int)singleton; } /** * Determine if this Interval is entirely within a given Interval. * * @return True if the bounds of this Interval are at or witin the given other Interval */ public boolean isSubrange(Interval other) { // Return true if the bounds of this Interval are at or within the given other Interval return this.low >= other.low && // This Interval begins at or within the given Interval, and this.high <= other.high; // This Interval ends at or before the given Interval } /** * Express this Interval object as text. * If the Interval is just 1 byte big, composes text with that byte's index, like "5". * If the Interval is a range of bytes, composes text like "5-10", this describes the 6 bytes between 5 and 11. * * @return A String */ public String toString() { // Compose text with the low and high ranges of this Interval object if (low == high) return String.valueOf(low); else return String.valueOf(low) + "-" + String.valueOf(high); } /** * Determine if this Interval is the same as a give one. * * @param o The Interval object to compare this one to * @return True if they are the same, false if different */ public boolean equals(Object o) { // Make sure the given object is also an Interval if (!(o instanceof Interval)) return false; // Return true if the low and high ranges match Interval other = (Interval)o; return low == other.low && high == other.high; } /** * Write this Interval object's low and high indices in a byte array. * * Makes an 8-byte array like LLLLHHHH. * LLLL and HHHH are 4-byte ints in big endian order. * LLLL is the low index, and HHHH is the high index. * * @return A byte array */ public byte[] toBytes() { // Make byte[] res = new byte[8]; // Make an array 8 bytes big toBytes(res, 0); return res; } /** * Write the low and high ranges of this Interval object to a given byte array. * Writes them like LLLLHHHH at the offset in the array. * Each is a 4-byte int in big endian order. * * @param dest A destination byte array this toBytes() method will write data in * @param offset The distance into the destionation byte array where toBytes() will start writing */ public void toBytes(byte[] dest, int offset) { // Write the low range as a 4-byte int, followed by the high range as a 4-byte int ByteOrder.int2beb(low, dest, offset); ByteOrder.int2beb(high, dest, offset + 4); // Add 4 to move beyond the low range we wrote first } }