package com.onionnetworks.fec; import com.onionnetworks.util.Util; import com.onionnetworks.util.Buffer; /** * * This class provides the main API/SPI for the FEC library. You cannot * construct an FECCode directly, rather you must use an FECCodeFactory. * * For example: * <code> * int k = 32; * int n = 256; * FECCode code = FECCodeFactory.getDefault().createFECCode(k,n); * </code> * * All FEC * implementations will sublcass FECCode and implement the encode/decode * methods. All codes implemented by this interface are assumed to be * systematic codes which means that the first k repair packets will be * the same as the original source packets. * * (c) Copyright 2001 Onion Networks * (c) Copyright 2000 OpenCola * * @author Justin F. Chapweske (justin@chapweske.com) */ public abstract class FECCode { protected int k,n; /** * Construct a new FECCode given <code>k</code> and <code>n</code> * * @param k The number of source packets to be encoded/decoded. * @param n The number of packets that the source packets will be encoded * to. */ protected FECCode(int k, int n) { this.k = k; this.n = n; } /** * This method takes an array of source packets and generates a number * of repair packets from them. This method could have taken in only * one repair packet to be generated, but in many cases it is more * efficient (and convenient) to encode multiple packets at once. This * is especially true of the NativeCode implementation where data must * be copied and the Java->Native->Java transition is expensive. * * @param src An array of <code>k</code> byte[]'s that contain the source * packets to be encoded. Often these byte[]'s are actually references * to a single byte[] that contains the entire source block, then you must * simply vary the srcOff's to pass it in in this fashion. src[0] will * point to the 1st packet, src[1] to the second, etc. * * @param srcOff An array of integers which specifies the offset of each * each packet within its associated byte[]. * * @param repair Much like src, variable points to a number of buffers * to which the encoded repair packets will be written. This array should * be the same length as repairOff and index. * * @param repairOff This is the repair analog to srcOff. * * @param index This int[] specifies the indexes of the packets to be * encoded and written to <code>repair</code>. These indexes must be * between 0..n (should probably be k..n, because encoding < k is a NOP) * * @param packetLength the packetLength in bytes. All of the buffers * in src and repair are assumed to be this long. */ protected abstract void encode(byte[][] src, int[] srcOff, byte[][] repair, int[] repairOff, int[] index, int packetLength); /** * This method takes an array of encoded packets and decodes them. Before * the packets are decoded, they are shuffled so that packets that are * original source packets (index < k) are positioned so that their * index within the byte[][] is the same as their packet index. If the * <code>shuffled</code> flag is set to true then it is assumed that * the packets are already in the proper order. If not then they will * have the references of the byte[]'s shuffled within the byte[][]. No * data will be copied, only references swapped. This means that if the * byte[][] is wrapping an underlying byte[] then the shuffling proceedure * may bring the byte[][] out of sync with the underlying data structure. * From an SPI perspective this means that the implementation is expected * to follow the exact same behavior as the shuffle() method in this class * which means that you should simply call shuffle() if the flag is false. * * @param pkts An array of <code>k</code> byte[]'s that contain the repair * packets to be decoded. The decoding proceedure will copy the decoded * data into the byte[]'s that are provided and will place them in order * within the byte[][]. If the byte[][] is already properly shuffled * then the byte[]'s will not be moved around in the byte[][]. * * @param pktsOff An array of integers which specifies the offset of each * each packet within its associated byte[]. * * @param index This int[] specifies the indexes of the packets to be * decoded. These indexes must be between 0..n * * @param packetLength the packetLength in bytes. All of the buffers * in pkts are assumed to be this long. */ protected abstract void decode(byte[][] pkts, int[] pktsOff, int[] index, int packetLength, boolean shuffled); /** * This method takes an array of source packets and generates a number * of repair packets from them. This method could have taken in only * one repair packet to be generated, but in many cases it is more * efficient (and convenient) to encode multiple packets at once. This * is especially true of the NativeCode implementation where data must * be copied and the Java->Native->Java transition is expensive. * * @param src An array of <code>k</code> Buffers that contain the source * packets to be encoded. Often these Buffers are actually references * to a single byte[] that contains the entire source block. * * @param repair Much like src, variable points to a number of Buffers * to which the encoded repair packets will be written. This array should * be the same length as index. * * @param index This int[] specifies the indexes of the packets to be * encoded and written to <code>repair</code>. These indexes must be * between 0..n (should probably be k..n, because encoding < k is a NOP) * */ public void encode(Buffer[] src, Buffer[] repair, int[] index) { byte[][] srcBufs = new byte[src.length][]; int[] srcOffs = new int[src.length]; byte[][] repairBufs = new byte[repair.length][]; int[] repairOffs = new int[repair.length]; for (int i=0;i<srcBufs.length;i++) { srcBufs[i] = src[i].b; srcOffs[i] = src[i].off; } for (int i=0;i<repairBufs.length;i++) { repairBufs[i] = repair[i].b; repairOffs[i] = repair[i].off; } encode(srcBufs,srcOffs,repairBufs,repairOffs,index,src[0].len); } /* protected void checkArguments() { if (index < 0 || index >= n) { throw new IllegalArgumentException("Invalid index "+index+ " (max "+(n-1)+")"); } if (buf.len != repair.len) { throw new IllegalArgumentException ("Source buffer and output buffer not same length"); } if (pkts.length != k || index.length != k) { throw new IllegalArgumentException("Must be exactly k packets "+ "and index entries."); } }*/ /** * This method takes an array of encoded packets and decodes them. Before * the packets are decoded, they are shuffled so that packets that are * original source packets (index < k) are positioned so that their * index within the byte[][] is the same as their packet index. * * We shuffle the packets using the copy mechanism to allow API users to * be guarenteed that the Buffer[] references will not be shuffled around. * This allows the Buffer[] to wrap an underlying byte[], and once * decoding is complete the entire block will be in the proper order * in the underlying byte[]. If the packets are already in the proper * position then no copying will take place. * * @param pkts An array of <code>k</code> Buffers that contain the repair * packets to be decoded. The decoding proceedure will copy the decoded * data into the Buffers that are provided and will place them in order * within the Buffer[]. If the Buffers are already properly shuffled * then no data will be copied around during the shuffle proceedure. * * @param index This int[] specifies the indexes of the packets to be * decoded. These indexes must be between 0..n * */ public void decode(Buffer[] pkts, int[] index) { // Must pre-shuffle so that no future shuffles bring the byte[]'s // out of sync with the Buffer[]'s. We use copyShuffle so that // the Buffer[]'s don't have their references shuffled around and // therefore we can have the Buffer[]'s wrapping one large byte[] // that will be decoded with all of the data in order in that block. copyShuffle(pkts,index,k); byte[][] bufs = new byte[pkts.length][]; int[] offs = new int[pkts.length]; for (int i=0;i<bufs.length;i++) { bufs[i] = pkts[i].b; offs[i] = pkts[i].off; } decode(bufs,offs,index,pkts[0].len,true); } /** * Move packets with index < k into their position. This method * copies the data using System.arraycopy rather than modifying the * Buffer[]. */ protected static final void copyShuffle(Buffer[] pkts, int index[], int k){ byte[] b = null; for (int i = 0;i < k ;) { if (index[i] >= k || index[i] == i) { i++; } else { // put pkts in the right position (first check for conflicts). int c = index[i]; if (index[c] == c) { throw new IllegalArgumentException ("Shuffle Error: Duplicate indexes at "+i); } // swap(index[c],index[i]) int tmp = index[i]; index[i] = index[c]; index[c] = tmp; // swap(pkts[c],pkts[i]) if (b == null) { b = new byte[pkts[0].len]; } System.arraycopy(pkts[i].b,pkts[i].off,b,0,b.length); System.arraycopy(pkts[c].b,pkts[c].off,pkts[i].b,pkts[i].off, b.length); System.arraycopy(b,0,pkts[c].b,pkts[c].off,b.length); } } } /** * shuffle move src packets in their position */ protected static final void shuffle(byte[][] pkts, int[] pktsOff, int[] index, int k) { for (int i=0; i<k;) { if (index[i] >= k || index[i] == i) { i++; } else { // put pkts in the right position (first check for conflicts). int c = index[i] ; if (index[c] == c) { throw new IllegalArgumentException("Shuffle error at "+i); } // swap(pkts[c],pkts[i]) byte[] tmp = pkts[i]; pkts[i] = pkts[c]; pkts[c] = tmp; // swap(pktsOff[c],pktsOff[i]) int tmp2 = pktsOff[i]; pktsOff[i] = pktsOff[c]; pktsOff[c] = tmp2; // swap(index[c],index[i]) tmp2 = index[i]; index[i] = index[c]; index[c] = tmp2; } } } }