+package iotcloud;
+import java.util.Vector;
+import java.nio.ByteBuffer;
+import javax.crypto.Mac;
+import java.util.Arrays;
+
+/**
+ * Data structuring for holding Slot information.
+ * @author Brian Demsky
+ * @version 1.0
+ */
+
+class Slot implements Liveness {
+ /** Sets the slot size. */
+ static final int SLOT_SIZE=2048;
+ /** Sets the size for the HMAC. */
+ static final int HMAC_SIZE=32;
+
+ /** Sequence number of the slot. */
+ private long seqnum;
+ /** HMAC of previous slot. */
+ private byte[] prevhmac;
+ /** HMAC of this slot. */
+ private byte[] hmac;
+ /** Machine that sent this slot. */
+ private long machineid;
+ /** Vector of entries in this slot. */
+ private Vector<Entry> entries;
+ /** Pieces of information that are live. */
+ private int livecount;
+ /** Flag that indicates whether this slot is still live for
+ * recording the machine that sent it. */
+ private boolean seqnumlive;
+ /** Number of bytes of free space. */
+ private int freespace;
+ /** Reference to Table */
+ private Table table;
+
+ Slot(Table _table, long _seqnum, long _machineid, byte[] _prevhmac, byte[] _hmac) {
+ seqnum=_seqnum;
+ machineid=_machineid;
+ prevhmac=_prevhmac;
+ hmac=_hmac;
+ entries=new Vector<Entry>();
+ livecount=1;
+ seqnumlive=true;
+ freespace = SLOT_SIZE - getBaseSize();
+ table=_table;
+ }
+
+ Slot(Table _table, long _seqnum, long _machineid, byte[] _prevhmac) {
+ this(_table, _seqnum, _machineid, _prevhmac, null);
+ }
+
+ Slot(Table _table, long _seqnum, long _machineid) {
+ this(_table, _seqnum, _machineid, new byte[HMAC_SIZE], null);
+ }
+
+ byte[] getHMAC() {
+ return hmac;
+ }
+
+ byte[] getPrevHMAC() {
+ return prevhmac;
+ }
+
+ void addEntry(Entry e) {
+ e=e.getCopy(this);
+ entries.add(e);
+ livecount++;
+ freespace -= e.getSize();
+ }
+
+ private void addShallowEntry(Entry e) {
+ entries.add(e);
+ livecount++;
+ freespace -= e.getSize();
+ }
+
+ /**
+ * Returns true if the slot has free space to hold the entry without
+ * using its reserved space. */
+
+ boolean hasSpace(Entry e) {
+ int newfreespace = freespace - e.getSize();
+ return newfreespace >= 0;
+ }
+
+ Vector<Entry> getEntries() {
+ return entries;
+ }
+
+ static Slot decode(Table table, byte[] array, Mac mac) {
+ mac.update(array, HMAC_SIZE, array.length-HMAC_SIZE);
+ byte[] realmac=mac.doFinal();
+
+ ByteBuffer bb=ByteBuffer.wrap(array);
+ byte[] hmac=new byte[HMAC_SIZE];
+ byte[] prevhmac=new byte[HMAC_SIZE];
+ bb.get(hmac);
+ bb.get(prevhmac);
+ if (!Arrays.equals(realmac, hmac))
+ throw new Error("Server Error: Invalid HMAC! Potential Attack!");
+
+ long seqnum=bb.getLong();
+ long machineid=bb.getLong();
+ int numentries=bb.getInt();
+ Slot slot=new Slot(table, seqnum, machineid, prevhmac, hmac);
+
+ for(int i=0; i<numentries; i++) {
+ slot.addShallowEntry(Entry.decode(slot, bb));
+ }
+
+ return slot;
+ }
+
+ byte[] encode(Mac mac) {
+ byte[] array=new byte[SLOT_SIZE];
+ ByteBuffer bb=ByteBuffer.wrap(array);
+ /* Leave space for the slot HMAC. */
+ bb.position(HMAC_SIZE);
+ bb.put(prevhmac);
+ bb.putLong(seqnum);
+ bb.putLong(machineid);
+ bb.putInt(entries.size());
+ for(Entry entry:entries) {
+ entry.encode(bb);
+ }
+ /* Compute our HMAC */
+ mac.update(array, HMAC_SIZE, array.length-HMAC_SIZE);
+ byte[] realmac=mac.doFinal();
+ hmac = realmac;
+ bb.position(0);
+ bb.put(realmac);
+ return array;
+ }
+
+ /**
+ * Returns the empty size of a Slot. Includes 2 HMACs, the machine
+ * identifier, the sequence number, and the number of entries.
+ */
+ int getBaseSize() {
+ return 2*HMAC_SIZE+2*Long.BYTES+Integer.BYTES;
+ }
+
+ /**
+ * Returns the live set of entries for this Slot. Generates a fake
+ * LastMessage entry to represent the information stored by the slot
+ * itself.
+ */
+
+ Vector<Entry> getLiveEntries(boolean resize) {
+ Vector<Entry> liveEntries=new Vector<Entry>();
+ for(Entry entry: entries) {
+ if (entry.isLive()) {
+ if (!resize || entry.getType() != Entry.TypeTableStatus)
+ liveEntries.add(entry);
+ }
+ }
+
+ if (seqnumlive && !resize)
+ liveEntries.add(new LastMessage(this, machineid, seqnum));
+
+ return liveEntries;
+ }
+
+ /**
+ * Returns the sequence number of the slot.
+ */
+
+ long getSequenceNumber() {
+ return seqnum;
+ }
+
+ /**
+ * Returns the machine that sent this slot.
+ */
+
+ long getMachineID() {
+ return machineid;
+ }
+
+ /**
+ * Records that a newer slot records the fact that this slot was
+ * sent by the relevant machine.
+ */
+
+ void setDead() {
+ seqnumlive=false;
+ decrementLiveCount();
+ }
+
+ /**
+ * Update the count of live entries.
+ */
+
+ void decrementLiveCount() {
+ livecount--;
+ if (livecount==0)
+ table.decrementLiveCount();
+ }
+
+ /**
+ * Returns whether the slot stores any live information.
+ */
+
+ boolean isLive() {
+ return livecount > 0;
+ }
+
+ public String toString() {
+ return "<"+getSequenceNumber()+">";
+ }
+}