mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Fixed the frame reordering window.
This commit is contained in:
@@ -2,21 +2,15 @@ package net.sf.briar.transport;
|
||||
|
||||
interface FrameWindow {
|
||||
|
||||
/** Returns true if the given number is too high to fit in the window. */
|
||||
boolean isTooHigh(long frameNumber);
|
||||
|
||||
/** Returns true if the given number is in the window. */
|
||||
boolean contains(long frameNumber);
|
||||
|
||||
/**
|
||||
* Advances the window so it is centred on the given number, unless the
|
||||
* centre is already greater than the number. Returns false if the window
|
||||
* is unmodifiable.
|
||||
*/
|
||||
boolean advance(long frameNumber);
|
||||
|
||||
/**
|
||||
* Removes the given number from the window and, if the number is greater
|
||||
* than or equal to the window's centre, advances the centre to one greater
|
||||
* than the given number. Returns false if the given number is not in the
|
||||
* window or the window is unmodifiable.
|
||||
* Removes the given number from the window and advances the window.
|
||||
* Returns false if the given number is not in the window.
|
||||
*/
|
||||
boolean remove(long frameNumber);
|
||||
}
|
||||
|
||||
@@ -5,18 +5,23 @@ import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
class FrameWindowImpl implements FrameWindow {
|
||||
|
||||
private final Collection<Long> window;
|
||||
|
||||
private long centre;
|
||||
private long base;
|
||||
|
||||
FrameWindowImpl() {
|
||||
window = new HashSet<Long>();
|
||||
for(long l = 0; l < FRAME_WINDOW_SIZE / 2; l++) window.add(l);
|
||||
centre = 0;
|
||||
fill(0, FRAME_WINDOW_SIZE);
|
||||
base = 0;
|
||||
}
|
||||
|
||||
public boolean isTooHigh(long frameNumber) {
|
||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
return frameNumber >= base + FRAME_WINDOW_SIZE;
|
||||
}
|
||||
|
||||
public boolean contains(long frameNumber) {
|
||||
@@ -25,39 +30,32 @@ class FrameWindowImpl implements FrameWindow {
|
||||
return window.contains(frameNumber);
|
||||
}
|
||||
|
||||
public boolean advance(long frameNumber) {
|
||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(frameNumber <= centre) return true;
|
||||
// Remove values that have passed out of the window
|
||||
long newBottom = getBottom(frameNumber);
|
||||
Iterator<Long> it = window.iterator();
|
||||
while(it.hasNext()) if(it.next() < newBottom) it.remove();
|
||||
// Add values that have passed into the window
|
||||
long fillFrom = Math.max(newBottom, getTop(centre) + 1);
|
||||
long newTop = getTop(frameNumber);
|
||||
for(long l = fillFrom; l <= newTop; l++) window.add(l);
|
||||
centre = frameNumber;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean remove(long frameNumber) {
|
||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(!window.remove(frameNumber)) return false;
|
||||
if(frameNumber >= centre && frameNumber < MAX_32_BIT_UNSIGNED)
|
||||
advance(frameNumber + 1);
|
||||
if(frameNumber == base) {
|
||||
// Find the new base
|
||||
if(window.isEmpty()) {
|
||||
base += FRAME_WINDOW_SIZE;
|
||||
fill(base, base + FRAME_WINDOW_SIZE);
|
||||
} else {
|
||||
for(long l = base; l < base + FRAME_WINDOW_SIZE; l++) {
|
||||
if(window.contains(l)) {
|
||||
fill(base + FRAME_WINDOW_SIZE, l + FRAME_WINDOW_SIZE);
|
||||
base = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the lowest value contained in a window with the given centre
|
||||
private static long getBottom(long centre) {
|
||||
return Math.max(0, centre - FRAME_WINDOW_SIZE / 2);
|
||||
}
|
||||
|
||||
// Returns the highest value contained in a window with the given centre
|
||||
private static long getTop(long centre) {
|
||||
return Math.min(MAX_32_BIT_UNSIGNED,
|
||||
centre + FRAME_WINDOW_SIZE / 2 - 1);
|
||||
private void fill(long from, long to) {
|
||||
for(long l = from; l < to; l++) {
|
||||
if(l <= MAX_32_BIT_UNSIGNED) window.add(l);
|
||||
else return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
class NullFrameWindow implements FrameWindow {
|
||||
|
||||
private long centre = 0L;
|
||||
private long base = 0L;
|
||||
|
||||
public boolean contains(long frameNumber) {
|
||||
return frameNumber == centre;
|
||||
public boolean isTooHigh(long frameNumber) {
|
||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
return frameNumber != base;
|
||||
}
|
||||
|
||||
public boolean advance(long frameNumber) {
|
||||
return frameNumber == centre;
|
||||
public boolean contains(long frameNumber) {
|
||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
return frameNumber == base;
|
||||
}
|
||||
|
||||
public boolean remove(long frameNumber) {
|
||||
if(frameNumber != centre) return false;
|
||||
centre++;
|
||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(frameNumber != base) return false;
|
||||
base++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
|
||||
public boolean readFrame(Frame f) throws IOException, InvalidDataException {
|
||||
if(!in.readFrame(f, window)) return false;
|
||||
long frameNumber = HeaderEncoder.getFrameNumber(f.getBuffer());
|
||||
if(!window.remove(frameNumber) && window.advance(frameNumber))
|
||||
throw new RuntimeException();
|
||||
if(!window.remove(frameNumber)) throw new IllegalStateException();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -13,77 +12,71 @@ public class FrameWindowImplTest extends BriarTestCase {
|
||||
FrameWindow w = new FrameWindowImpl();
|
||||
for(int i = 0; i < 100; i++) {
|
||||
assertTrue(w.contains(i));
|
||||
w.remove(i);
|
||||
assertTrue(w.remove(i));
|
||||
assertFalse(w.contains(i));
|
||||
}
|
||||
for(int i = 100; i < 100 + FRAME_WINDOW_SIZE; i++) {
|
||||
assertTrue(w.contains(i));
|
||||
assertFalse(w.isTooHigh(i));
|
||||
}
|
||||
assertFalse(w.contains(100 + FRAME_WINDOW_SIZE));
|
||||
assertTrue(w.isTooHigh(100 + FRAME_WINDOW_SIZE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowJumping() {
|
||||
FrameWindow w = new FrameWindowImpl();
|
||||
for(int i = 0; i < 100; i += 13) {
|
||||
// Base of the window is 0
|
||||
for(int i = 0; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.contains(i));
|
||||
assertFalse(w.contains(FRAME_WINDOW_SIZE));
|
||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
|
||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
|
||||
// Remove all numbers except 0 and 5
|
||||
for(int i = 1; i < 5; i++) assertTrue(w.remove(i));
|
||||
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.remove(i));
|
||||
// Base of the window should still be 0
|
||||
assertTrue(w.contains(0));
|
||||
for(int i = 1; i < 5; i++) assertFalse(w.contains(i));
|
||||
assertTrue(w.contains(5));
|
||||
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
|
||||
assertFalse(w.contains(FRAME_WINDOW_SIZE));
|
||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
|
||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
|
||||
// Remove 0
|
||||
assertTrue(w.remove(0));
|
||||
// Base of the window should now be 5
|
||||
for(int i = 0; i < 5; i++) assertFalse(w.contains(i));
|
||||
assertTrue(w.contains(5));
|
||||
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
|
||||
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
|
||||
assertTrue(w.contains(i));
|
||||
w.remove(i);
|
||||
}
|
||||
assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
|
||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
|
||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
|
||||
// Remove all numbers except 5
|
||||
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
|
||||
assertTrue(w.remove(i));
|
||||
}
|
||||
// Base of the window should still be 5
|
||||
assertTrue(w.contains(5));
|
||||
for(int i = 6; i < FRAME_WINDOW_SIZE + 5; i++) {
|
||||
assertFalse(w.contains(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowUpperLimit() {
|
||||
FrameWindow w = new FrameWindowImpl();
|
||||
// Centre is 0, highest value in window is 15
|
||||
for(int i = 0; i < 16; i++) assertTrue(w.contains(i));
|
||||
assertFalse(w.remove(16));
|
||||
assertTrue(w.remove(15));
|
||||
assertFalse(w.remove(15));
|
||||
// Centre is 16, highest value in window is 31
|
||||
for(int i = 0; i < 32; i++) assertEquals(i != 15, w.contains(i));
|
||||
assertFalse(w.remove(32));
|
||||
assertTrue(w.remove(31));
|
||||
// Values greater than 2^32 - 1 should never be allowed
|
||||
assertTrue(w.advance(MAX_32_BIT_UNSIGNED - 1));
|
||||
assertTrue(w.remove(MAX_32_BIT_UNSIGNED - 1));
|
||||
assertTrue(w.remove(MAX_32_BIT_UNSIGNED));
|
||||
try {
|
||||
w.remove(MAX_32_BIT_UNSIGNED + 1);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvance() {
|
||||
FrameWindow w = new FrameWindowImpl();
|
||||
long centre = 0;
|
||||
for(int i = 0; i < 10; i++) {
|
||||
long bottom = Math.max(0, centre - FRAME_WINDOW_SIZE / 2);
|
||||
long top = Math.min(MAX_32_BIT_UNSIGNED,
|
||||
centre + FRAME_WINDOW_SIZE / 2 - 1);
|
||||
if(bottom > 0) assertFalse(w.contains(bottom - 1));
|
||||
for(long j = bottom; j <= top; j++) assertTrue(w.contains(j));
|
||||
if(top < MAX_32_BIT_UNSIGNED) assertFalse(w.contains(top + 1));
|
||||
centre += 12345;
|
||||
assertTrue(w.advance(centre));
|
||||
assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
|
||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
|
||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
|
||||
// Remove 5
|
||||
assertTrue(w.remove(5));
|
||||
// Base of the window should now be FRAME_WINDOW_SIZE + 5
|
||||
for(int i = 0; i < FRAME_WINDOW_SIZE + 5; i++) {
|
||||
assertFalse(w.contains(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowLowerLimit() {
|
||||
FrameWindow w = new FrameWindowImpl();
|
||||
// Centre is 0, negative values should never be allowed
|
||||
try {
|
||||
w.remove(-1);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
// Slide the window twice
|
||||
assertTrue(w.remove(15));
|
||||
assertTrue(w.remove(16));
|
||||
// Centre is 17, lowest value in window is 1
|
||||
assertFalse(w.remove(0));
|
||||
assertTrue(w.remove(1));
|
||||
// Slide the window
|
||||
assertTrue(w.remove(25));
|
||||
// Centre is 26, lowest value in window is 10
|
||||
assertFalse(w.remove(9));
|
||||
assertTrue(w.remove(10));
|
||||
for(int i = FRAME_WINDOW_SIZE + 5; i < FRAME_WINDOW_SIZE * 2 + 5; i++) {
|
||||
assertTrue(w.contains(i));
|
||||
}
|
||||
assertFalse(w.contains(FRAME_WINDOW_SIZE * 2 + 5));
|
||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 4));
|
||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 5));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user