package org.jgroups.util;
import org.jgroups.Address;
import java.util.Arrays;
import java.util.Map;
/**
* A mutable version of Digest. This class is not synchronized because only a single thread at a time will access it
* @author Bela Ban
*/
public class MutableDigest extends Digest {
protected static final double RESIZE_FACTOR=1.2;
protected boolean sealed=false;
protected int current_index=0; // points to the next index to be written
protected MutableDigest(Address[] members, long[] seqnos, int current_index) {
super(members, seqnos);
this.current_index=current_index;
}
/** Used for externalization */
public MutableDigest() {
super();
}
public MutableDigest(int size) {
createArrays(size);
}
public MutableDigest(Map<Address,long[]> map) {
super(map);
current_index=map.size();
}
public MutableDigest(Digest digest) {
super(digest);
current_index=digest.size();
}
public void add(Address member, long highest_delivered_seqno, long highest_received_seqno) {
add(member, highest_delivered_seqno, highest_received_seqno, true);
}
public void add(Address member, long highest_delivered_seqno, long highest_received_seqno, boolean replace) {
if(member == null)
return;
checkSealed();
if(replace) {
int index=find(member); // see if the member is already present
if(index >= 0) {
seqnos[index * 2]=highest_delivered_seqno;
seqnos[index * 2 +1]=highest_received_seqno;
return;
}
}
if(current_index >= members.length)
resize();
members[current_index]=member;
seqnos[current_index * 2]=highest_delivered_seqno;
seqnos[current_index * 2 +1]=highest_received_seqno;
current_index++;
}
public void add(Digest digest) {
add(digest, true);
}
public void add(Digest digest, boolean replace) {
if(digest == null)
return;
checkSealed();
for(DigestEntry entry: digest)
add(entry.getMember(), entry.getHighestDeliveredSeqno(), entry.getHighestReceivedSeqno(), replace);
}
public void replace(Digest d) {
if(d == null)
return;
clear();
createArrays(d.size());
add(d, false); // don't search for existing elements, because there won't be any !
}
public MutableDigest copy() {
return new MutableDigest(Arrays.copyOf(members, members.length), Arrays.copyOf(seqnos, seqnos.length), current_index);
}
/**
* Adds a digest to this digest. This digest must have enough space to add the other digest; otherwise an error
* message will be written. For each sender in the other digest, the merge() method will be called.
*/
public void merge(Digest digest) {
if(digest == null)
return;
checkSealed();
for(DigestEntry entry: digest)
merge(entry.getMember(), entry.getHighestDeliveredSeqno(), entry.getHighestReceivedSeqno());
}
/**
* Similar to add(), but if the sender already exists, its seqnos will be modified (no new entry) as follows:
* <ol>
* <li>this.highest_delivered_seqno=max(this.highest_delivered_seqno, highest_delivered_seqno)
* <li>this.highest_received_seqno=max(this.highest_received_seqno, highest_received_seqno)
* </ol>
* If the member doesn not exist, a new entry will be added (provided there is enough space)
*/
public void merge(Address member, long highest_delivered_seqno, long highest_received_seqno) {
if(member == null)
return;
checkSealed();
long[] entry=get(member);
if(entry == null)
add(member, highest_delivered_seqno, highest_received_seqno, false); // don't replace as member wasn't found
else {// replaces existing entry
add(member, Math.max(entry[0], highest_delivered_seqno), Math.max(entry[1], highest_received_seqno));
}
}
/** Increments the sender's highest delivered seqno by 1 */
public void incrementHighestDeliveredSeqno(Address member) {
long[] entry=get(member);
if(entry == null)
return;
checkSealed();
long new_highest_delivered=entry[0] +1;
// highest_received must be >= highest_delivered, but not smaller !
long new_highest_received=Math.max(entry[1], new_highest_delivered);
add(member, new_highest_delivered, new_highest_received, true); // replace
}
public void clear() {
checkSealed();
current_index=0;
}
public void setHighestDeliveredAndSeenSeqnos(Address member, long highest_delivered_seqno, long highest_received_seqno) {
long[] entry=get(member);
if(entry != null) {
checkSealed();
add(member, highest_delivered_seqno, highest_received_seqno, true); // replace existing entry
}
}
/** Seals the instance against modifications */
public void seal() {
sealed=true;
}
public int size() {
return current_index;
}
protected void resize() {
int new_size=Math.max((int)(members.length * RESIZE_FACTOR), members.length +1);
members=Arrays.copyOf(members, new_size);
seqnos=Arrays.copyOf(seqnos, new_size * 2);
}
protected final void checkSealed() {
if(sealed)
throw new IllegalAccessError("instance has been sealed and cannot be modified");
}
}