package net.i2p.crypto;

import gnu.crypto.hash.Sha256Standalone;

import java.util.ArrayList;
import java.util.List;

import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.Hash;

/** 
 * Defines a wrapper for SHA-256 operation.  All the good stuff occurs
 * in the GNU-Crypto {@link gnu.crypto.hash.Sha256Standalone}
 * 
 */
public final class SHA256Generator {
    private List _digests;
    private final List _digestsGnu;
    public SHA256Generator(I2PAppContext context) {
        _digests = new ArrayList(32);
        _digestsGnu = new ArrayList(32);
    }
    
    public static final SHA256Generator getInstance() {
        return I2PAppContext.getGlobalContext().sha();
    }
    
    /** Calculate the SHA-256 has of the source
     * @param source what to hash
     * @return hash of the source
     */
    public final Hash calculateHash(byte[] source) {
        return calculateHash(source, 0, source.length);
    }
    public final Hash calculateHash(byte[] source, int start, int len) {
        Sha256Standalone digest = acquireGnu();
        digest.update(source, start, len);
        byte rv[] = digest.digest();
        releaseGnu(digest);
        return new Hash(rv);
    }
    
    public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
        Sha256Standalone digest = acquireGnu();
        digest.update(source, start, len);
        byte rv[] = digest.digest();
        releaseGnu(digest);
        System.arraycopy(rv, 0, out, outOffset, rv.length);
    }
    
    private Sha256Standalone acquireGnu() {
        Sha256Standalone rv = null;
        synchronized (_digestsGnu) {
            if (!_digestsGnu.isEmpty())
                rv = (Sha256Standalone)_digestsGnu.remove(0);
        }
        if (rv != null)
            rv.reset();
        else
            rv = new Sha256Standalone();
        return rv;
    }
    
    private void releaseGnu(Sha256Standalone digest) {
        synchronized (_digestsGnu) {
            if (_digestsGnu.size() < 32) {
                _digestsGnu.add(digest);
            }
        }
    }
    
    public static void main(String args[]) {
        I2PAppContext ctx = I2PAppContext.getGlobalContext();
        for (int i = 0; i < args.length; i++)
            System.out.println("SHA256 [" + args[i] + "] = [" + Base64.encode(ctx.sha().calculateHash(args[i].getBytes()).getData()) + "]");
    }
}
@@ -81,40 +81,38 @@
 til.
-ArrayList;%0Aimport java.util.List
+concurrent.LinkedBlockingQueue
 ;%0A%0Ai
@@ -394,45 +394,51 @@
 ate 
-List _digests;%0A    private final List
+final LinkedBlockingQueue%3CSha256Standalone%3E
  _di
@@ -519,65 +519,37 @@
 ests
+Gnu
  = new 
-ArrayList(32);%0A        _digestsGnu = new ArrayList
+LinkedBlockingQueue
 (32)
@@ -1596,162 +1596,27 @@
 v = 
-null;%0A        synchronized (_digestsGnu) %7B%0A            if (!_digestsGnu.isEmpty())%0A                rv = (Sha256Standalone)_digestsGnu.remove(0);%0A        %7D
+_digestsGnu.poll();
 %0A   
@@ -1811,144 +1811,34 @@
     
-synchronized (_digestsGnu) %7B%0A            if (_digestsGnu.size() %3C 32) %7B%0A                _digestsGnu.add(digest);%0A            %7D%0A        %7D
+_digestsGnu.offer(digest);
 %0A   
@@ -2146,8 +2146,9 @@
 %0A    %7D%0A%7D
+%0A

concurrentify with LBQ