package org.jgroups.util; import org.jgroups.Global; import java.io.DataInput; import java.io.DataOutput; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * A list of sequence numbers (seqnos). Seqnos have to be added in ascending order, and can be single seqnos * or seqno ranges (e.g. [5-10]). This class is unsynchronized. Note that for serialization, we assume that the * lowest and highest seqno in the list are not more than 2 ^ 31 apart. * @author Bela Ban * @since 3.1 */ public class SeqnoList implements Streamable, Iterable<Long> { protected final List<Seqno> seqnos=new ArrayList<Seqno>(); public SeqnoList() { } public SeqnoList(long seqno) { add(seqno); } public SeqnoList(long from, long to) { add(from, to); } /** Adds a single seqno */ public SeqnoList add(long seqno) { seqnos.add(new Seqno(seqno)); return this; } public SeqnoList add(long ... seqnos) { if(seqnos != null) { for(long seqno: seqnos) add(seqno); } return this; } /** Adds a seqno range */ public SeqnoList add(long from, long to) { seqnos.add(new SeqnoRange(from, to)); return this; } /** Removes all seqnos <= seqno */ public void remove(long min_seqno) { for(Iterator<Seqno> it=seqnos.iterator(); it.hasNext();) { Seqno tmp=it.next(); if(tmp instanceof SeqnoRange) { SeqnoRange range=(SeqnoRange)tmp; if(range.to <= min_seqno) it.remove(); else { if(range.from <= min_seqno) range.from=min_seqno+1; } } else { if(tmp.from <= min_seqno) it.remove(); } } } /** Removes all seqnos > seqno */ public void removeHigherThan(long max_seqno) { for(Iterator<Seqno> it=seqnos.iterator(); it.hasNext();) { Seqno tmp=it.next(); if(tmp instanceof SeqnoRange) { SeqnoRange range=(SeqnoRange)tmp; if(range.from > max_seqno) it.remove(); else { if(range.to > max_seqno) range.to=max_seqno; } } else { if(tmp.from > max_seqno) it.remove(); } } } /** Returns the last seqno, this should also be the highest seqno in the list as we're supposed to add seqnos * in order * @return */ public long getLast() { int size=seqnos.size(); if(size == 0) return 0; Seqno seqno=seqnos.get(size - 1); return seqno instanceof SeqnoRange? ((SeqnoRange)seqno).to : seqno.from; } public void writeTo(DataOutput out) throws Exception { out.writeInt(seqnos.size()); for(Seqno seqno: seqnos) { if(seqno instanceof SeqnoRange) { SeqnoRange range=(SeqnoRange)seqno; out.writeBoolean(true); Util.writeLongSequence(range.from, range.to, out); } else { out.writeBoolean(false); Util.writeLong(seqno.from, out); } } } public void readFrom(DataInput in) throws Exception { int len=in.readInt(); for(int i=0; i < len; i++) { if(in.readBoolean()) { long[] tmp=Util.readLongSequence(in); seqnos.add(new SeqnoRange(tmp[0], tmp[1])); } else { seqnos.add(new Seqno(Util.readLong(in))); } } } public int serializedSize() { int retval=Global.INT_SIZE // number of elements in seqnos + seqnos.size() * Global.BYTE_SIZE; // plus 1 boolean (Seqno or SeqnoRange) per element for(Seqno seqno: seqnos) { if(seqno instanceof SeqnoRange) { SeqnoRange range=(SeqnoRange)seqno; retval+=Util.size(range.from, range.to); } else { retval+=Util.size(seqno.from); } } return retval; } public int size() { int retval=0; for(Seqno seqno: seqnos) { if(seqno instanceof SeqnoRange) { SeqnoRange range=(SeqnoRange)seqno; retval+=(range.to - range.from +1); } else retval++; } return retval; } public String toString() { return seqnos.toString(); } public Iterator<Long> iterator() { return new SeqnoListIterator(); } protected static class Seqno { protected long from; public Seqno(long num) { this.from=num; } public String toString() { return String.valueOf(from); } } protected static class SeqnoRange extends Seqno { protected long to; public SeqnoRange(long from, long to) { super(from); this.to=to; if(to < from) throw new IllegalArgumentException("to (" + to + ") needs to be >= from (" + from + ")"); } public String toString() { return super.toString() + "-" + to; } } protected class SeqnoListIterator implements Iterator<Long> { protected int index=0; protected SeqnoRange range=null; protected long range_index=-1; public boolean hasNext() { return (range != null && range_index < range.to) || index < seqnos.size(); } public Long next() { if(range != null) { if(range_index < range.to) return ++range_index; else range=null; } if(index >= seqnos.size()) throw new NoSuchElementException("index " + index + " is >= size " + seqnos.size()); Seqno next=seqnos.get(index++); if(next instanceof SeqnoRange) { range=(SeqnoRange)next; range_index=range.from; return range_index; } else return next.from; } public void remove() { // not supported } } }