private String rangesAsString() {
   assert !ranges.isEmpty();
   StringBuilder sb = new StringBuilder();
   AbstractType at = (AbstractType) ranges.comparator();
   assert at != null;
   for (RangeTombstone i : ranges) {
     sb.append("[");
     sb.append(at.getString(i.min)).append("-");
     sb.append(at.getString(i.max)).append(", ");
     sb.append(i.data);
     sb.append("]");
   }
   return sb.toString();
 }
 public DeletionInfo(long markedForDeleteAt, int localDeletionTime) {
   // Pre-1.1 node may return MIN_VALUE for non-deleted container, but the new default is MAX_VALUE
   // (see CASSANDRA-3872)
   this(
       new DeletionTime(
           markedForDeleteAt,
           localDeletionTime == Integer.MIN_VALUE ? Integer.MAX_VALUE : localDeletionTime),
       IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());
 }
 /**
  * Return a new DeletionInfo correspond to purging every tombstones that are older than {@code
  * gcbefore}.
  *
  * @param gcBefore timestamp (in seconds) before which tombstones should be purged
  * @return a new DeletionInfo with the purged info remove. Should return DeletionInfo.LIVE if no
  *     tombstones remain.
  */
 public DeletionInfo purge(int gcBefore) {
   if (ranges.isEmpty()) {
     return topLevel.localDeletionTime < gcBefore ? LIVE : this;
   } else {
     // We rebuild a new intervalTree that contains only non expired range tombstones
     List<RangeTombstone> nonExpired = new ArrayList<RangeTombstone>();
     for (RangeTombstone range : ranges) {
       if (range.data.localDeletionTime >= gcBefore) nonExpired.add(range);
     }
     IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> newRanges =
         nonExpired.size() == ranges.intervalCount()
             ? ranges
             : IntervalTree.build(nonExpired, ranges.comparator());
     return topLevel.localDeletionTime < gcBefore
         ? new DeletionInfo(DeletionTime.LIVE, newRanges)
         : new DeletionInfo(topLevel, newRanges);
   }
 }
  public boolean isDeleted(ByteBuffer name, long timestamp) {
    if (isLive()) return false;
    if (timestamp <= topLevel.markedForDeleteAt) return true;

    for (DeletionTime d : ranges.search(name)) {
      if (timestamp <= d.markedForDeleteAt) return true;
    }
    return false;
  }
 static {
   try {
     Constructor<RangeTombstone> constructor =
         RangeTombstone.class.getConstructor(
             ByteBuffer.class, ByteBuffer.class, DeletionTime.class);
     itSerializer = IntervalTree.serializer(bbSerializer, DeletionTime.serializer, constructor);
   } catch (NoSuchMethodException e) {
     throw new RuntimeException(e);
   }
 }
    public DeletionInfo deserialize(DataInput in, int version, Comparator<ByteBuffer> comparator)
        throws IOException {
      assert comparator != null;
      DeletionTime topLevel = DeletionTime.serializer.deserialize(in);
      if (version < MessagingService.VERSION_12)
        return new DeletionInfo(
            topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());

      IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges =
          itSerializer.deserialize(in, version, comparator);
      return new DeletionInfo(topLevel, ranges);
    }
 /** Returns a new DeletionInfo containing of this plus the provided {@code newInfo}. */
 public DeletionInfo add(DeletionInfo newInfo) {
   if (ranges.isEmpty()) {
     return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt
         ? newInfo
         : newInfo.ranges.isEmpty() ? this : new DeletionInfo(topLevel, newInfo.ranges);
   } else {
     if (newInfo.ranges.isEmpty()) {
       return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt
           ? new DeletionInfo(newInfo.topLevel, ranges)
           : this;
     } else {
       // Need to merge both ranges
       Set<RangeTombstone> merged = new HashSet<RangeTombstone>();
       Iterables.addAll(merged, Iterables.concat(ranges, newInfo.ranges));
       return new DeletionInfo(
           topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt
               ? newInfo.topLevel
               : topLevel,
           IntervalTree.build(merged, ranges.comparator()));
     }
   }
 }
 /** Returns whether this DeletionInfo is live, that is deletes no columns. */
 public boolean isLive() {
   return topLevel.markedForDeleteAt == Long.MIN_VALUE
       && topLevel.localDeletionTime == Integer.MAX_VALUE
       && ranges.isEmpty();
 }
 public DeletionInfo(DeletionTime topLevel) {
   this(topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());
 }
 public DeletionInfo(RangeTombstone rangeTombstone, Comparator<ByteBuffer> comparator) {
   this(
       DeletionTime.LIVE,
       IntervalTree.build(Collections.<RangeTombstone>singletonList(rangeTombstone), comparator));
   assert comparator != null;
 }
public class DeletionInfo {
  private static final Serializer serializer = new Serializer();

  // We don't have way to represent the full interval of keys (Interval don't support the minimum
  // token as the right bound),
  // so we keep the topLevel deletion info separatly. This also slightly optimize the case of full
  // row deletion which is rather common.
  private final DeletionTime topLevel;
  private final IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges;

  public static final DeletionInfo LIVE =
      new DeletionInfo(
          DeletionTime.LIVE, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());

  public DeletionInfo(long markedForDeleteAt, int localDeletionTime) {
    // Pre-1.1 node may return MIN_VALUE for non-deleted container, but the new default is MAX_VALUE
    // (see CASSANDRA-3872)
    this(
        new DeletionTime(
            markedForDeleteAt,
            localDeletionTime == Integer.MIN_VALUE ? Integer.MAX_VALUE : localDeletionTime),
        IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());
  }

  public DeletionInfo(
      ByteBuffer start,
      ByteBuffer end,
      Comparator<ByteBuffer> comparator,
      long markedForDeleteAt,
      int localDeletionTime) {
    this(
        new RangeTombstone(start, end, new DeletionTime(markedForDeleteAt, localDeletionTime)),
        comparator);
  }

  public DeletionInfo(RangeTombstone rangeTombstone, Comparator<ByteBuffer> comparator) {
    this(
        DeletionTime.LIVE,
        IntervalTree.build(Collections.<RangeTombstone>singletonList(rangeTombstone), comparator));
    assert comparator != null;
  }

  public DeletionInfo(DeletionTime topLevel) {
    this(topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());
  }

  private DeletionInfo(
      DeletionTime topLevel, IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges) {
    this.topLevel = topLevel;
    this.ranges = ranges;
  }

  public static Serializer serializer() {
    return serializer;
  }

  /** Returns whether this DeletionInfo is live, that is deletes no columns. */
  public boolean isLive() {
    return topLevel.markedForDeleteAt == Long.MIN_VALUE
        && topLevel.localDeletionTime == Integer.MAX_VALUE
        && ranges.isEmpty();
  }

  /**
   * Return whether a given column is deleted by the container having this deletion info.
   *
   * @param column the column to check.
   * @return true if the column is deleted, false otherwise
   */
  public boolean isDeleted(IColumn column) {
    return isDeleted(column.name(), column.mostRecentLiveChangeAt());
  }

  public boolean isDeleted(ByteBuffer name, long timestamp) {
    if (isLive()) return false;
    if (timestamp <= topLevel.markedForDeleteAt) return true;

    for (DeletionTime d : ranges.search(name)) {
      if (timestamp <= d.markedForDeleteAt) return true;
    }
    return false;
  }

  /**
   * Return a new DeletionInfo correspond to purging every tombstones that are older than {@code
   * gcbefore}.
   *
   * @param gcBefore timestamp (in seconds) before which tombstones should be purged
   * @return a new DeletionInfo with the purged info remove. Should return DeletionInfo.LIVE if no
   *     tombstones remain.
   */
  public DeletionInfo purge(int gcBefore) {
    if (ranges.isEmpty()) {
      return topLevel.localDeletionTime < gcBefore ? LIVE : this;
    } else {
      // We rebuild a new intervalTree that contains only non expired range tombstones
      List<RangeTombstone> nonExpired = new ArrayList<RangeTombstone>();
      for (RangeTombstone range : ranges) {
        if (range.data.localDeletionTime >= gcBefore) nonExpired.add(range);
      }
      IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> newRanges =
          nonExpired.size() == ranges.intervalCount()
              ? ranges
              : IntervalTree.build(nonExpired, ranges.comparator());
      return topLevel.localDeletionTime < gcBefore
          ? new DeletionInfo(DeletionTime.LIVE, newRanges)
          : new DeletionInfo(topLevel, newRanges);
    }
  }

  /** Returns a new DeletionInfo containing of this plus the provided {@code newInfo}. */
  public DeletionInfo add(DeletionInfo newInfo) {
    if (ranges.isEmpty()) {
      return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt
          ? newInfo
          : newInfo.ranges.isEmpty() ? this : new DeletionInfo(topLevel, newInfo.ranges);
    } else {
      if (newInfo.ranges.isEmpty()) {
        return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt
            ? new DeletionInfo(newInfo.topLevel, ranges)
            : this;
      } else {
        // Need to merge both ranges
        Set<RangeTombstone> merged = new HashSet<RangeTombstone>();
        Iterables.addAll(merged, Iterables.concat(ranges, newInfo.ranges));
        return new DeletionInfo(
            topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt
                ? newInfo.topLevel
                : topLevel,
            IntervalTree.build(merged, ranges.comparator()));
      }
    }
  }

  public long minTimestamp() {
    long minTimestamp = topLevel.markedForDeleteAt;
    for (RangeTombstone i : ranges) {
      minTimestamp = Math.min(minTimestamp, i.data.markedForDeleteAt);
    }
    return minTimestamp;
  }

  /** The maximum timestamp mentioned by this DeletionInfo. */
  public long maxTimestamp() {
    long maxTimestamp = topLevel.markedForDeleteAt;
    for (RangeTombstone i : ranges) {
      maxTimestamp = Math.max(maxTimestamp, i.data.markedForDeleteAt);
    }
    return maxTimestamp;
  }

  public DeletionTime getTopLevelDeletion() {
    return topLevel;
  }

  public Iterator<RangeTombstone> rangeIterator() {
    return ranges.iterator();
  }

  public int dataSize() {
    int size = TypeSizes.NATIVE.sizeof(topLevel.markedForDeleteAt);
    for (RangeTombstone r : ranges) {
      size += r.min.remaining() + r.max.remaining();
      size += TypeSizes.NATIVE.sizeof(r.data.markedForDeleteAt);
    }
    return size;
  }

  @Override
  public String toString() {
    if (ranges.isEmpty()) return String.format("{%s}", topLevel);
    else return String.format("{%s, ranges=%s}", topLevel, rangesAsString());
  }

  private String rangesAsString() {
    assert !ranges.isEmpty();
    StringBuilder sb = new StringBuilder();
    AbstractType at = (AbstractType) ranges.comparator();
    assert at != null;
    for (RangeTombstone i : ranges) {
      sb.append("[");
      sb.append(at.getString(i.min)).append("-");
      sb.append(at.getString(i.max)).append(", ");
      sb.append(i.data);
      sb.append("]");
    }
    return sb.toString();
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof DeletionInfo)) return false;
    DeletionInfo that = (DeletionInfo) o;
    return topLevel.equals(that.topLevel) && ranges.equals(that.ranges);
  }

  @Override
  public final int hashCode() {
    return Objects.hashCode(topLevel, ranges);
  }

  public static class Serializer
      implements IVersionedSerializer<DeletionInfo>, ISSTableSerializer<DeletionInfo> {
    private static final ISerializer<ByteBuffer> bbSerializer =
        new ISerializer<ByteBuffer>() {
          public void serialize(ByteBuffer bb, DataOutput dos) throws IOException {
            ByteBufferUtil.writeWithShortLength(bb, dos);
          }

          public ByteBuffer deserialize(DataInput dis) throws IOException {
            return ByteBufferUtil.readWithShortLength(dis);
          }

          public long serializedSize(ByteBuffer bb, TypeSizes typeSizes) {
            int bbSize = bb.remaining();
            return typeSizes.sizeof((short) bbSize) + bbSize;
          }
        };

    private static final IntervalTree.Serializer<ByteBuffer, DeletionTime, RangeTombstone>
        itSerializer;

    static {
      try {
        Constructor<RangeTombstone> constructor =
            RangeTombstone.class.getConstructor(
                ByteBuffer.class, ByteBuffer.class, DeletionTime.class);
        itSerializer = IntervalTree.serializer(bbSerializer, DeletionTime.serializer, constructor);
      } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
      }
    }

    public void serialize(DeletionInfo info, DataOutput out, int version) throws IOException {
      DeletionTime.serializer.serialize(info.topLevel, out);
      // Pre-1.2 version don't know about range tombstones and thus users should upgrade all
      // nodes before using them. If they didn't, better fail early that propagating bad info
      if (version < MessagingService.VERSION_12) {
        if (!info.ranges.isEmpty())
          throw new RuntimeException(
              "Cannot send range tombstone to pre-1.2 node. You should upgrade all node to Cassandra 1.2+ before using range tombstone.");
        // Otherwise we're done
      } else {
        itSerializer.serialize(info.ranges, out, version);
      }
    }

    public void serializeForSSTable(DeletionInfo info, DataOutput out) throws IOException {
      DeletionTime.serializer.serialize(info.topLevel, out);
    }

    /*
     * Range tombstones internally depend on the column family serializer, but it is not serialized.
     * Thus deserialize(DataInput, int, Comparator<ByteBuffer>) should be used instead of this method.
     */
    public DeletionInfo deserialize(DataInput in, int version) throws IOException {
      throw new UnsupportedOperationException();
    }

    public DeletionInfo deserialize(DataInput in, int version, Comparator<ByteBuffer> comparator)
        throws IOException {
      assert comparator != null;
      DeletionTime topLevel = DeletionTime.serializer.deserialize(in);
      if (version < MessagingService.VERSION_12)
        return new DeletionInfo(
            topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());

      IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges =
          itSerializer.deserialize(in, version, comparator);
      return new DeletionInfo(topLevel, ranges);
    }

    public DeletionInfo deserializeFromSSTable(DataInput in, Descriptor.Version version)
        throws IOException {
      DeletionTime topLevel = DeletionTime.serializer.deserialize(in);
      return new DeletionInfo(
          topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());
    }

    public long serializedSize(DeletionInfo info, TypeSizes typeSizes, int version) {
      long size = DeletionTime.serializer.serializedSize(info.topLevel, typeSizes);
      if (version < MessagingService.VERSION_12) return size;

      return size + itSerializer.serializedSize(info.ranges, typeSizes, version);
    }

    public long serializedSize(DeletionInfo info, int version) {
      return serializedSize(info, TypeSizes.NATIVE, version);
    }
  }
}
 public DeletionInfo deserializeFromSSTable(DataInput in, Descriptor.Version version)
     throws IOException {
   DeletionTime topLevel = DeletionTime.serializer.deserialize(in);
   return new DeletionInfo(
       topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree());
 }
 @Override
 public boolean equals(Object o) {
   if (!(o instanceof DeletionInfo)) return false;
   DeletionInfo that = (DeletionInfo) o;
   return topLevel.equals(that.topLevel) && ranges.equals(that.ranges);
 }
 @Override
 public String toString() {
   if (ranges.isEmpty()) return String.format("{%s}", topLevel);
   else return String.format("{%s, ranges=%s}", topLevel, rangesAsString());
 }
 public Iterator<RangeTombstone> rangeIterator() {
   return ranges.iterator();
 }