001 package edu.harvard.deas.hyperenc; 002 003 import java.security.MessageDigest; 004 import java.util.ArrayList; 005 import java.util.Collections; 006 import java.util.HashMap; 007 import java.util.LinkedList; 008 import java.util.List; 009 import java.util.Map; 010 011 012 /** 013 * Stores unreconciled pages and corresponding hash values, reconciled pages, 014 * system blocks, and encryption blocks for a single contact. 015 * <p> 016 * <b>Thread safety:</b> This implementation is unconditionally thread-safe. 017 */ 018 public class BasicHyperStorage extends HyperStorage { 019 /** Indicates if this storage is the master of the communication path */ 020 private Direction direction; 021 022 /** 023 * Collection of system blocks. 024 */ 025 private Map<Integer, byte[][]> sysblocks; 026 027 /** 028 * Collection of encryption blocks. Must synchronize on this map when 029 * removing; see <code>remEncBlockList</code>. 030 */ 031 // XXX would be better to make an EncryptionBlockMap to enforce this 032 private Map<Integer, byte[]> encblocks; 033 034 /** 035 * Collection of unreconciled pages. 036 */ 037 private Map<Integer, byte[]> upages; 038 039 /** 040 * Hashes of unreconciled pages; keys correspond to those of <code>upages</code>. 041 */ 042 private Map<Integer, byte[]> uhashes; 043 044 /** 045 * Collection of reconciled pages. 046 */ 047 private List<byte[]> rpages; 048 049 /** Provider of our random data. Should be either WebRandomSource or VSATRandomSource */ 050 private RandomSource source; 051 052 /** Used to compute hashes of the unreconciled pages */ 053 private MessageDigest digest; 054 055 /** Used to shuffle page. */ 056 private PageShuffler shuffle; 057 058 /** 059 * A counter that returns a different integer every time getNext() is called, 060 * even if it's called from different threads. 061 */ 062 private static class Counter { 063 private int id = 0; 064 public Counter() {} 065 public synchronized int getNext() { return id++; } 066 } 067 068 /** Keeps track of the next id to assign to a system block */ 069 private Counter nextsysid; 070 071 /** Keeps track of the next id to assign to an encryption block */ 072 private Counter nextencid; 073 074 /** The partner for which this Storage stores things. */ 075 private Contact contact; 076 077 /** Constructor. 078 * @param contact The partner for whom this storage stores things 079 * @param direction Indicates if this storage is the master of the communication path 080 * @param rs The random source used to create pages for this path of 081 * communication 082 */ 083 public BasicHyperStorage(Contact contact, Direction direction, RandomSource 084 rs, MessageDigest md, PageShuffler ps) { 085 this.direction = direction; 086 this.contact = contact; 087 sysblocks = Collections.synchronizedMap(new HashMap<Integer, byte[][]>()); 088 encblocks = Collections.synchronizedMap(new HashMap<Integer, byte[]>()); 089 uhashes = Collections.synchronizedMap(new HashMap<Integer, byte[]>()); 090 upages = Collections.synchronizedMap(new HashMap<Integer, byte[]>()); 091 rpages = Collections.synchronizedList(new LinkedList<byte[]>()); 092 source = rs; 093 digest = md; 094 shuffle = ps; 095 096 // Start at the very beginning. A very good place to start 097 nextsysid = new Counter(); 098 nextencid = new Counter(); 099 } 100 101 // INSERTION INTO STORAGE 102 103 @Override 104 public int addSysBlock(byte [][] blocks) { 105 // COPY BLOCK 106 byte[][] copyblocks = new byte[blocks.length][blocks[0].length]; 107 for (int i=0; i<blocks.length; i++) { 108 for (int j=0; j<blocks[i].length; j++) { 109 copyblocks[i][j]=blocks[i][j]; 110 } 111 } 112 113 int id = nextsysid.getNext(); 114 sysblocks.put(id, copyblocks); 115 return id; 116 } 117 118 @Override 119 public int addEncBlock(byte [] block){ 120 // COPY BLOCK 121 byte[] copyblock = new byte[block.length]; 122 for (int j=0; j<block.length; j++) { 123 copyblock[j]=block[j]; 124 } 125 126 int id = nextencid.getNext(); 127 encblocks.put(id, copyblock); 128 return id; 129 } 130 131 @Override 132 public void addUPage(int id, byte [] page, byte [] hash) { 133 upages.put(id, page); 134 uhashes.put(id, hash); 135 } 136 137 @Override 138 public void addRPage(byte [] page){ 139 rpages.add(page); 140 } 141 142 // QUERY ABOUT STORAGE 143 144 // These methods must use synchronized statements because keySet() iterates 145 // over the keys in each map, and we must prevent other threads from modifying 146 // the collection during the iteration. 147 148 @Override 149 public List<Integer> getSysBlockList() { 150 synchronized (sysblocks) { 151 return new LinkedList<Integer>(sysblocks.keySet()); 152 } 153 } 154 155 @Override 156 public List<Integer> getEncBlockList() { 157 synchronized (encblocks) { 158 return new LinkedList<Integer>(encblocks.keySet()); 159 } 160 } 161 162 @Override 163 public List<Integer> getUPageList() { 164 synchronized (upages) { 165 return new ArrayList<Integer>(upages.keySet()); 166 } 167 } 168 169 // RETRIEVAL FROM STORAGE 170 171 @Override 172 public byte [][] getSysBlock(int id) { 173 return sysblocks.get(id); 174 } 175 176 @Override 177 public byte [] getEncBlock(int id) { 178 return encblocks.get(id); 179 } 180 181 @Override 182 public byte [] getUPage(int id) { 183 return upages.get(id); 184 } 185 186 @Override 187 public byte [] getUHash(int id) { 188 return uhashes.get(id); 189 } 190 191 // REMOVAL FROM STORAGE 192 193 @Override 194 public byte[][] remSysBlock(int id) { 195 return sysblocks.remove(id); 196 } 197 198 @Override 199 public byte[] remEncBlock(int id) { 200 // Operations that remove from encblocks need to synchronize on the map so 201 // that remEncBlockList can guarantee atomicity. 202 synchronized (encblocks) { 203 return encblocks.remove(id); 204 } 205 } 206 207 @Override 208 public List<byte[]> remEncBlockList(List<Integer> idlist) 209 throws BlockMissingException { 210 synchronized (encblocks) { 211 List<Integer> missinglist = new ArrayList<Integer>(); 212 for (int id : idlist) { 213 if (!encblocks.containsKey(id)) 214 missinglist.add(id); 215 } 216 if (!missinglist.isEmpty()) 217 throw new BlockMissingException(missinglist, missinglist.size() 218 + " required encryption blocks were missing"); 219 220 // All IDs are valid; proceed to remove the blocks 221 List<byte[]> blocklist = new ArrayList<byte[]>(); 222 for (int id : idlist) { 223 byte[] b = encblocks.remove(id); 224 assert(b != null); 225 blocklist.add(b); 226 } 227 return blocklist; 228 } 229 } 230 231 @Override 232 public byte[] remUPage(int id) { 233 uhashes.remove(id); 234 return upages.remove(id); 235 } 236 237 @Override 238 public byte[] remRPage() { 239 return rpages.remove(0); 240 } 241 242 // SIZE QUERIES 243 244 @Override 245 public int numSysBlocks() { 246 return sysblocks.size(); 247 } 248 249 @Override 250 public int numEncBlocks() { 251 return encblocks.size(); 252 } 253 254 @Override 255 public int numUPages() { 256 return upages.size(); 257 } 258 259 @Override 260 public int numRPages() { 261 return rpages.size(); 262 } 263 264 // PROPERTIES 265 266 @Override 267 public Contact getContact() { 268 return contact; 269 } 270 271 @Override 272 public RandomSource getSource() { 273 return source; 274 } 275 276 @Override 277 public MessageDigest getDigest() { 278 return digest; 279 } 280 281 @Override 282 public PageShuffler getShuffler() { 283 return shuffle; 284 } 285 286 @Override 287 public Direction getDirection(){ 288 return direction; 289 } 290 291 }