package com.limegroup.gnutella.util; import java.util.ArrayList; import java.util.List; import com.google.inject.Inject; import com.google.inject.Singleton; import com.onionnetworks.fec.FECCode; import com.onionnetworks.fec.FECCodeFactory; import com.onionnetworks.util.Buffer; /** * Encapsulates lots of the FEC-related ugliness. */ @Singleton public class FECUtilsImpl implements FECUtils { /** The factory that creates the FEC code. Inited lazily */ private FECCodeFactory fecFactory; @Inject public FECUtilsImpl() { System.setProperty("com.onionnetworks.fec.keys", "pure8,pure16"); // disable native libs } public List<byte[]> encode(byte[] data, int packetSize, float redundancy) { if (fecFactory == null) fecFactory = FECCodeFactory.getDefault(); int numPackets = data.length / packetSize + (data.length % packetSize == 0 ? 0 : 1); int numChecksums = (int)Math.ceil(numPackets * redundancy); FECCode code = fecFactory.createFECCode(numPackets, numChecksums); Buffer [] chunks = new Buffer[numPackets]; Buffer [] out = new Buffer[numChecksums]; int [] indices = new int[numChecksums]; for (int i = 0; i < numPackets; i++) { byte [] b = new byte[packetSize]; System.arraycopy(data, i * packetSize, b, 0, Math.min(packetSize, data.length - i * packetSize)); chunks[i] = new Buffer(b); out[i] = new Buffer(new byte[packetSize]); indices[i] = i; } for (int i = numPackets; i < numChecksums; i++) { out[i] = new Buffer(new byte[packetSize]); indices[i] = i; } code.encode(chunks, out, indices); List<byte []> ret = new ArrayList<byte[]>(numChecksums); for (Buffer buf : out) ret.add(buf.b); return ret; } public byte[] decode(List<byte []> packets, int size) { if (fecFactory == null) fecFactory = FECCodeFactory.getDefault(); int packetSize = -1; List<Integer> indices = new ArrayList<Integer>(packets.size()); List<Buffer> buffers = new ArrayList<Buffer>(packets.size()); for (int i = 0; i < packets.size(); i++) { if (packets.get(i) == null) continue; buffers.add(new Buffer(packets.get(i))); indices.add(i); if (packetSize == -1) packetSize = packets.get(i).length; else if (packets.get(i).length != packetSize) return null; // not gonna work. } if (packetSize == -1) return null; // no packets at all? int [] indicesArray = new int[indices.size()]; for (int i = 0; i < indicesArray.length; i++) indicesArray[i] = indices.get(i); Buffer [] bufferArray = new Buffer[buffers.size()]; buffers.toArray(bufferArray); int numPackets = size / packetSize + (size % packetSize == 0 ? 0 : 1); FECCode code = fecFactory.createFECCode(numPackets, packets.size()); try { code.decode(bufferArray, indicesArray); } catch (Throwable decodeFailed) { // bad API return null; } byte [] ret = new byte[size]; for (int i = 0; i < numPackets; i++) { byte [] b = bufferArray[i].b; System.arraycopy(b,0,ret, i * packetSize, Math.min(packetSize, size - i * packetSize)); } return ret; } }