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    }