org.jgroups.util.Digest Maven / Gradle / Ivy
package org.jgroups.util;
import org.jgroups.Address;
import org.jgroups.Constructable;
import org.jgroups.Global;
import org.jgroups.annotations.Immutable;
import java.io.DataInput;
import java.io.DataOutput;
import java.util.*;
import java.util.function.Supplier;
import static java.lang.Math.max;
/**
* A message digest containing - for each member - the highest seqno delivered (hd) and the highest seqno received (hr).
* The seqnos are stored according to the order of the members in the associated membership, ie. seqnos[0] is the hd for
* member members[0], seqnos[1] is the hr for the same member, seqnos[2] is the hd for members[1] and so on.
* Field 'members' may refer to the View.members, e.g. in a JoinRsp where we ship a view and a digest referring to
* the view's membership. This is done to conserve memory.
* This class is immutable except for 2 cases:
*
* - The contents are read when unmarshalling (readFrom())
* - The membership is set with members(). This must only be done directly after unmarshalling
*
* @author Bela Ban
*/
@Immutable
public class Digest implements SizeStreamable, Iterable, Constructable {
// Stores the members corresponding to the seqnos. Example: members[2] --> hd=seqnos[4], hr=seqnos[5]
protected Address[] members;
// Stores highest delivered and received seqnos. This array is double the size of members. We store HD-HR pairs,
// so to get the HD seqno for member P at index i --> seqnos[i*2], to get the HR --> seqnos[i*2 +1]
protected long[] seqnos;
/** Used for serialization */
public Digest() {
}
public Digest(final Address[] members, long[] seqnos) {
if(members == null) throw new IllegalArgumentException("members is null");
if(seqnos == null) throw new IllegalArgumentException("seqnos is null");
this.members=members;
this.seqnos=seqnos;
checkPostcondition();
}
/** Only used internally, don't use ! */
public Digest(final Address[] members) {
if(members == null) throw new IllegalArgumentException("members is null");
this.members=members;
}
/** Only used for testing */
public Digest(Digest digest) {
if(digest == null)
return;
this.members=digest.members; // the members list is immutable
this.seqnos=(digest instanceof MutableDigest || this instanceof MutableDigest)?
Arrays.copyOf((digest).seqnos, digest.seqnos.length) : digest.seqnos;
checkPostcondition();
}
/** Creates a new digest from an existing map by copying the keys and values from map */
public Digest(Map map) {
createArrays(map);
checkPostcondition();
}
public Digest(Address sender, long highest_delivered, long highest_received) {
members=new Address[]{sender};
seqnos=new long[]{highest_delivered,highest_received};
}
public Supplier extends Digest> create() {
return Digest::new;
}
/** Don't use, this method is reserved for Bela ! :-) */
public Address[] getMembersRaw() {
return members;
}
public int capacity() {
return members != null? members.length : 0;
}
public boolean contains(Address mbr) {
if(mbr == null || members == null)
return false;
for(Address member: members)
if(Objects.equals(member, mbr))
return true;
return false;
}
public boolean containsAll(Address ... members) {
for(Address member: members)
if(!contains(member))
return false;
return true;
}
/** 2 digests are equal if their memberships match and all highest-delivered and highest-received seqnos match */
public boolean equals(Object obj) {
if(this == obj)
return true;
Digest other=(Digest)obj;
// return Arrays.equals(members, other.members) && Arrays.equals(seqnos,other.seqnos);
boolean same_mbrs=Arrays.equals(members, other.members);
boolean same_seqnos=Arrays.equals(seqnos, other.seqnos);
return same_mbrs && same_seqnos;
}
/**
* Returns the highest delivered and received seqnos associated with a member.
* @param member
* @return An array of 2 elements: highest_delivered and highest_received seqnos
*/
public long[] get(Address member) {
int index=find(member);
if(index < 0)
return null;
return new long[]{seqnos[index * 2], seqnos[index * 2 +1]};
}
public Iterator iterator() {
return new MyIterator();
}
public Digest copy() {
return new Digest(members, Arrays.copyOf(seqnos, seqnos.length));
}
public void writeTo(DataOutput out) throws Exception {
writeTo(out, true);
}
public void writeTo(DataOutput out, boolean write_addrs) throws Exception {
if(write_addrs)
Util.writeAddresses(members, out);
else
out.writeShort(members.length);
for(int i=0; i < capacity(); i++)
Bits.writeLongSequence(seqnos[i * 2], seqnos[i * 2 +1], out);
}
public void readFrom(DataInput in) throws Exception {
readFrom(in, true);
}
public void readFrom(DataInput in, boolean read_addrs) throws Exception {
if(read_addrs) {
members=Util.readAddresses(in);
seqnos=new long[capacity() * 2];
}
else
seqnos=new long[in.readShort() *2];
for(int i=0; i < seqnos.length/2; i++)
Bits.readLongSequence(in, seqnos, i*2);
}
public int serializedSize() {
return (int)serializedSize(true);
}
public long serializedSize(boolean with_members) {
long retval=with_members? Util.size(members) : Global.SHORT_SIZE;
for(int i=0; i < members.length; i++)
retval+=Bits.size(seqnos[i*2], seqnos[i*2+1]);
return retval;
}
public String toString() {
return toString(members, true);
}
public String toString(final Digest order) {
return order != null? toString(order.members, true) : toString(members, true);
}
public String toString(final Address[] order, boolean print_highest_received) {
StringBuilder sb=new StringBuilder();
boolean first=true;
if(capacity() == 0) return "[]";
int count=0, capacity=capacity();
for(Address key: order) {
long[] tmp_seqnos=key != null? get(key) : null;
if(key == null || tmp_seqnos == null)
continue;
if(!first)
sb.append(", ");
else
first=false;
sb.append(key).append(": ").append('[').append(tmp_seqnos[0]);
if(print_highest_received)
sb.append(" (").append(tmp_seqnos[1]).append(")");
sb.append("]");
if(Util.MAX_LIST_PRINT_SIZE > 0 && ++count >= Util.MAX_LIST_PRINT_SIZE) {
if(capacity > count)
sb.append(", ...");
break;
}
}
return sb.toString();
}
protected int find(Address mbr) {
if(mbr == null || members == null)
return -1;
for(int i=0; i < members.length; i++) {
Address member=members[i];
if(Objects.equals(member, mbr))
return i;
}
return -1;
}
protected void createArrays(Map map) {
int size=map.size();
members=new Address[size];
seqnos=new long[size * 2];
int index=0;
for(Map.Entry entry: map.entrySet()) {
members[index]=entry.getKey();
seqnos[index * 2 ]=entry.getValue()[0];
seqnos[index * 2 +1]=entry.getValue()[1];
index++;
}
}
protected void checkPostcondition() {
int size=members.length;
if(size*2 != seqnos.length)
throw new IllegalArgumentException("seqnos.length (" + seqnos.length + ") is not twice the members size (" + size + ")");
}
protected class MyIterator implements Iterator {
protected int index;
public boolean hasNext() {
return index < capacity();
}
public Entry next() {
if(index >= capacity())
throw new NoSuchElementException("index=" + index + ", capacity=" + capacity());
Address mbr=members != null? members[index] : null;
long hd=seqnos != null? seqnos[index*2] : 0, hr=seqnos != null? seqnos[index*2+1] : 0;
Entry entry=new Entry(mbr, hd, hr);
index++;
return entry;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
/** Keeps track of one members plus its highest delivered and received seqnos */
@Immutable
public static class Entry {
protected final Address member;
protected final long hd;
protected final long hr;
public Entry(Address member, long highest_delivered, long highest_received) {
this.member=member;
this.hd=highest_delivered;
this.hr=highest_received;
}
public Address getMember() {return member;}
public long getHighestDeliveredSeqno() {return hd;}
public long getHighestReceivedSeqno() {return hr;}
public long getHighest() {return max(hd,hr);}
public boolean equals(Object obj) {
Entry other=(Entry)obj;
return member.equals(other.member) && hd == other.hd && hr == other.hr;
}
public String toString() {
return member + ": [" + hd + " (" + hr + ")]";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy