package com.limegroup.gnutella.messages; import java.util.ArrayList; import java.util.List; import org.limewire.collection.IntervalSet; import org.limewire.core.settings.SharingSettings; import org.limewire.io.BadGGEPPropertyException; import org.limewire.io.GGEP; import org.limewire.util.ByteUtils; /** * Contains the logic for writing and reading IntervalSets * to and from a GGEP field. */ public class IntervalEncoder { public static void encode(long size, GGEP g, IntervalSet s) { List<Integer> bytes = new ArrayList<Integer>(); List<Integer> shorts = new ArrayList<Integer>(); List<Integer> b24 = new ArrayList<Integer>(); List<Integer> ints = new ArrayList<Integer>(); for (int i : s.encode(size)) { long l = i & 0xFFFFFFFF; if (l > 0xFFFFFFL) ints.add(i); else if (l > 0xFFFF) b24.add(i); else if (l > 0xFF) shorts.add(i); else bytes.add(i); } byte [] bytesB = new byte[bytes.size()]; for (int i = 0; i < bytesB.length; i++) bytesB[i] = (byte)bytes.get(i).intValue(); byte [] shortsB = new byte[shorts.size() * 2]; for (int i = 0; i < shorts.size(); i++) ByteUtils.short2beb(shorts.get(i).shortValue(), shortsB, i * 2); byte [] b24B = new byte[b24.size() * 3]; for (int i = 0; i < b24.size(); i++) { int value = b24.get(i); b24B[i*3] = (byte)((value & 0xFF0000) >> 16); b24B[i*3 + 1] = (byte)((value & 0xFF00) >> 8); b24B[i*3 + 2] = (byte)(value & 0xFF); } byte [] intsB = new byte[ints.size() * 4]; for (int i = 0; i < ints.size(); i++) ByteUtils.int2beb(ints.get(i).intValue(), intsB, i * 4); int availableSpace = SharingSettings.MAX_PARTIAL_ENCODING_SIZE.getValue(); availableSpace = addIfSpace(bytesB,g,1,availableSpace); availableSpace = addIfSpace(shortsB,g,2,availableSpace); availableSpace = addIfSpace(b24B,g,3,availableSpace); addIfSpace(intsB,g,4,availableSpace); // special case - for an empty interval set we add an extention if (bytes.size() + shorts.size() + b24.size() + ints.size() == 0) g.put(GGEPKeys.GGEP_HEADER_PARTIAL_RESULT_PREFIX+"0"); } /** * adds the byte array to the appropriate ggep value if there is enough space * @param dataSize the size of each entry * @param available how much space we have available * @return how much space is left after adding */ private static int addIfSpace(byte [] toAdd, GGEP ggep, int dataSize, int available) { if (toAdd.length == 0 || available <= 0) return available; assert toAdd.length % dataSize == 0; if (toAdd.length > available) { byte [] tmp = new byte[available - (available % dataSize)]; if (tmp.length == 0) return available; System.arraycopy(toAdd,0,tmp,0,tmp.length); toAdd = tmp; } ggep.put(GGEPKeys.GGEP_HEADER_PARTIAL_RESULT_PREFIX+dataSize,toAdd); return available - toAdd.length; } /** * @return an IntervalSet contained in this GGEP. Null if none. */ public static IntervalSet decode(long size, GGEP ggep) throws BadGGEPPropertyException{ if (ggep.hasKey(GGEPKeys.GGEP_HEADER_PARTIAL_RESULT_PREFIX+"0")) return new IntervalSet(); IntervalSet ret = null; for (int i = 1; i <= 4; i++ ) { String key = GGEPKeys.GGEP_HEADER_PARTIAL_RESULT_PREFIX+i; if (ggep.hasValueFor(key)) { byte [] b = ggep.get(key); if (ret == null) ret = new IntervalSet(); // is data valid? if (b.length % i != 0) return null; for (int j = 0; j < b.length; j+=i) { int nodeId = 0; for (int k = 0; k < i; k++) { nodeId <<= 8; nodeId |= (b[j + k] & 0xFF); } ret.decode(size, nodeId); } } } return ret; } }