package peergos.shared.user.fs.erasure; import java.io.ByteArrayOutputStream; import java.util.*; import peergos.shared.util.StringUtils; public class Erasure { public static byte[][] split(byte[] input, int originalBlobs, int allowedFailures) { return split(input, new GaloisField256(), originalBlobs, allowedFailures); } public static byte[][] split(byte[] input, GaloisField f, int originalBlobs, int allowedFailures) { long t1 = System.currentTimeMillis(); int[] ints = convert(input, f); int n = originalBlobs + allowedFailures*2; ByteArrayOutputStream[] bouts = new ByteArrayOutputStream[n]; for (int i=0; i < bouts.length; i++) bouts[i] = new ByteArrayOutputStream(); int encodeSize = (f.size()/n)*n; int inputSize = encodeSize*originalBlobs/n; int nec = encodeSize-inputSize; int symbolSize = inputSize/originalBlobs; if (symbolSize * originalBlobs != inputSize) throw new IllegalStateException(StringUtils.format("Bad alignment of bytes in chunking. %d != %d * %d", inputSize, symbolSize, originalBlobs)); for (int i=0; i < ints.length; i+=inputSize) { int[] copy = Arrays.copyOfRange(ints, i, i+inputSize); byte[] encoded = convert(GaloisPolynomial.encode(copy, nec, f), f); for (int j=0; j < n; j++) { bouts[j].write(encoded, j*symbolSize, symbolSize); } } byte[][] res = new byte[n][]; for (int i=0; i < n; i++) res[i] = bouts[i].toByteArray(); long t2 = System.currentTimeMillis(); System.out.println("Erasure encoding took "+(t2-t1)+ " mS"); return res; } public static byte[] recombine(byte[][] encoded, int truncateTo, int originalBlobs, int allowedFailures) { return recombine(new GaloisField256(), encoded, truncateTo, originalBlobs, allowedFailures); } public static byte[] recombine(List<byte[]> encoded, int truncateTo, int originalBlobs, int allowedFailures) { return recombine(new GaloisField256(), encoded.toArray(new byte[0][]), truncateTo, originalBlobs, allowedFailures); } public static byte[] recombine(GaloisField f, byte[][] encoded, int truncateTo, int originalBlobs, int allowedFailures) { long t1 = System.currentTimeMillis(); try { int n = originalBlobs + allowedFailures * 2; int encodeSize = (f.size() / n) * n; int inputSize = encodeSize * originalBlobs / n; int nec = encodeSize - inputSize; int symbolSize = inputSize / originalBlobs; if (encoded.length == 0) return new byte[0]; int tbSize = encoded[0].length; // don't bother in the case where we haven't lost any of the original fragments for (int k = 0; k < originalBlobs; k++) { if (encoded[k] == null || encoded[k].length == 0) break; if (k == originalBlobs - 1) { // shortcut ByteArrayOutputStream res = new ByteArrayOutputStream(); for (int i = 0; i < tbSize; i += symbolSize) { for (int j = 0; j < originalBlobs; j++) res.write(encoded[j], i, symbolSize); } return Arrays.copyOfRange(res.toByteArray(), 0, truncateTo); } } ByteArrayOutputStream res = new ByteArrayOutputStream(); for (int i = 0; i < tbSize; i += symbolSize) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); // take a symbol from each stream for (int j = 0; j < n; j++) bout.write(encoded[j], i, symbolSize); int[] decodedInts = GaloisPolynomial.decode(convert(bout.toByteArray(), f), nec, f); byte[] raw = convert(decodedInts, f); res.write(raw, 0, inputSize); } return Arrays.copyOfRange(res.toByteArray(), 0, truncateTo); } finally { long t2 = System.currentTimeMillis(); System.out.println("Erasure decoding took " + (t2 - t1) + " mS"); } } public static int[] convert(byte[] in, GaloisField f) { if (f.size() >= 256) { int[] res = new int[in.length]; for (int i = 0; i < in.length; i++) res[i] = f.mask() & in[i]; return res; } if (f.size() == 16) { int[] res = new int[in.length*2]; for (int i = 0; i < in.length; i++) { res[2*i] = f.mask() & in[i]; res[2*i+1] = f.mask() & (in[i] >> 4); } return res; } throw new IllegalStateException("Unimplemented GaloisField size conversion"); } public static byte[] convert(int[] in, GaloisField f) { if (f.size() >= 256) { byte[] res = new byte[in.length]; for (int i = 0; i < in.length; i++) res[i] = (byte) in[i]; return res; } if (f.size() == 16) { byte[] res = new byte[in.length/2]; for (int i = 0; i < res.length; i++) res[i] = (byte) (in[2*i] | (in[2*i+1] << 4)); return res; } throw new IllegalStateException("Unimplemented GaloisField size conversion"); } }