Merge packet list and window into cache.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
package packetcache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const BufSize = 1500
|
||||
|
||||
type entry struct {
|
||||
seqno uint16
|
||||
length int
|
||||
buf [BufSize]byte
|
||||
}
|
||||
|
||||
type Cache struct {
|
||||
mu sync.Mutex
|
||||
first uint16 // the first seqno
|
||||
bitmap uint32
|
||||
tail int // the next entry to be rewritten
|
||||
entries []entry
|
||||
}
|
||||
|
||||
func New(capacity int) *Cache {
|
||||
return &Cache{
|
||||
entries: make([]entry, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// Set a bit in the bitmap, shifting first if necessary.
|
||||
func (cache *Cache) set(seqno uint16) {
|
||||
if cache.bitmap == 0 {
|
||||
cache.first = seqno
|
||||
cache.bitmap = 1
|
||||
return
|
||||
}
|
||||
|
||||
if ((seqno - cache.first) & 0x8000) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if seqno == cache.first {
|
||||
cache.bitmap >>= 1
|
||||
cache.first += 1
|
||||
for (cache.bitmap & 1) == 1 {
|
||||
cache.bitmap >>= 1
|
||||
cache.first += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if seqno-cache.first < 32 {
|
||||
cache.bitmap |= (1 << uint16(seqno-cache.first))
|
||||
return
|
||||
}
|
||||
|
||||
shift := seqno - cache.first - 31
|
||||
cache.bitmap >>= shift
|
||||
cache.first += shift
|
||||
cache.bitmap |= (1 << uint16(seqno-cache.first))
|
||||
return
|
||||
}
|
||||
|
||||
// Store a packet, setting bitmap at the same time
|
||||
func (cache *Cache) Store(seqno uint16, buf []byte) uint16 {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
cache.set(seqno)
|
||||
|
||||
cache.entries[cache.tail].seqno = seqno
|
||||
copy(cache.entries[cache.tail].buf[:], buf)
|
||||
cache.entries[cache.tail].length = len(buf)
|
||||
cache.tail = (cache.tail + 1) % len(cache.entries)
|
||||
|
||||
return cache.first
|
||||
}
|
||||
|
||||
func (cache *Cache) Get(seqno uint16) []byte {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
for i := range cache.entries {
|
||||
if cache.entries[i].length == 0 ||
|
||||
cache.entries[i].seqno != seqno {
|
||||
continue
|
||||
}
|
||||
buf := make([]byte, cache.entries[i].length)
|
||||
copy(buf, cache.entries[i].buf[:])
|
||||
return buf
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shift 17 bits out of the bitmap, return first index and remaining 16.
|
||||
func (cache *Cache) BitmapGet() (uint16, uint16) {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
first := cache.first
|
||||
bitmap := uint16((cache.bitmap >> 1) & 0xFFFF)
|
||||
cache.bitmap >>= 17
|
||||
cache.first += 17
|
||||
return first, bitmap
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package packetcache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
)
|
||||
|
||||
func randomBuf() []byte {
|
||||
length := rand.Int31n(BufSize-1) + 1
|
||||
buf := make([]byte, length)
|
||||
rand.Read(buf)
|
||||
return buf
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
buf1 := randomBuf()
|
||||
buf2 := randomBuf()
|
||||
cache := New(16)
|
||||
cache.Store(13, buf1)
|
||||
cache.Store(17, buf2)
|
||||
|
||||
if bytes.Compare(cache.Get(13), buf1) != 0 {
|
||||
t.Errorf("Couldn't get 13")
|
||||
}
|
||||
if bytes.Compare(cache.Get(17), buf2) != 0 {
|
||||
t.Errorf("Couldn't get 17")
|
||||
}
|
||||
if cache.Get(42) != nil {
|
||||
t.Errorf("Creation ex nihilo")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheOverflow(t *testing.T) {
|
||||
cache := New(16)
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
cache.Store(uint16(i), []byte{uint8(i)})
|
||||
}
|
||||
|
||||
for i := 0; i < 32; i++ {
|
||||
buf := cache.Get(uint16(i))
|
||||
if i < 16 {
|
||||
if buf != nil {
|
||||
t.Errorf("Creation ex nihilo: %v", i)
|
||||
}
|
||||
} else {
|
||||
if len(buf) != 1 || buf[0] != uint8(i) {
|
||||
t.Errorf("Expected [%v], got %v", i, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitmap(t *testing.T) {
|
||||
value := uint64(0xcdd58f1e035379c0)
|
||||
packet := make([]byte, 1)
|
||||
|
||||
cache := New(16)
|
||||
|
||||
var first uint16
|
||||
for i := 0; i < 64; i++ {
|
||||
if (value & (1 << i)) != 0 {
|
||||
first = cache.Store(uint16(42 + i), packet)
|
||||
}
|
||||
}
|
||||
|
||||
value >>= uint16(first - 42)
|
||||
if uint32(value) != cache.bitmap {
|
||||
t.Errorf("Got %b, expected %b", cache.bitmap, value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitmapWrap(t *testing.T) {
|
||||
value := uint64(0xcdd58f1e035379c0)
|
||||
packet := make([]byte, 1)
|
||||
|
||||
cache := New(16)
|
||||
|
||||
cache.Store(0x7000, packet)
|
||||
cache.Store(0xA000, packet)
|
||||
|
||||
var first uint16
|
||||
for i := 0; i < 64; i++ {
|
||||
if (value & (1 << i)) != 0 {
|
||||
first = cache.Store(uint16(42 + i), packet)
|
||||
}
|
||||
}
|
||||
|
||||
value >>= uint16(first - 42)
|
||||
if uint32(value) != cache.bitmap {
|
||||
t.Errorf("Got %b, expected %b", cache.bitmap, value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitmapGet(t *testing.T) {
|
||||
value := uint64(0xcdd58f1e035379c0)
|
||||
packet := make([]byte, 1)
|
||||
|
||||
cache := New(16)
|
||||
|
||||
for i := 0; i < 64; i++ {
|
||||
if (value & (1 << i)) != 0 {
|
||||
cache.Store(uint16(42 + i), packet)
|
||||
}
|
||||
}
|
||||
|
||||
pos := uint16(42)
|
||||
for cache.bitmap != 0 {
|
||||
first, bitmap := cache.BitmapGet()
|
||||
if first < pos || first >= pos+64 {
|
||||
t.Errorf("First is %v, pos is %v", first, pos)
|
||||
}
|
||||
value >>= (first - pos)
|
||||
pos = first
|
||||
if (value & 1) != 0 {
|
||||
t.Errorf("Value is odd")
|
||||
}
|
||||
value >>= 1
|
||||
pos += 1
|
||||
if bitmap != uint16(value&0xFFFF) {
|
||||
t.Errorf("Got %b, expected %b", bitmap, (value & 0xFFFF))
|
||||
}
|
||||
value >>= 16
|
||||
pos += 16
|
||||
}
|
||||
if value != 0 {
|
||||
t.Errorf("Value is %v", value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitmapPacket(t *testing.T) {
|
||||
value := uint64(0xcdd58f1e035379c0)
|
||||
packet := make([]byte, 1)
|
||||
|
||||
cache := New(16)
|
||||
|
||||
for i := 0; i < 64; i++ {
|
||||
if (value & (1 << i)) != 0 {
|
||||
cache.Store(uint16(42 + i), packet)
|
||||
}
|
||||
}
|
||||
|
||||
first, bitmap := cache.BitmapGet()
|
||||
|
||||
p := rtcp.NackPair{first, rtcp.PacketBitmap(^bitmap)}
|
||||
pl := p.PacketList()
|
||||
|
||||
for _, s := range pl {
|
||||
if s < 42 || s >= 42+64 {
|
||||
if (value & (1 << (s - 42))) != 0 {
|
||||
t.Errorf("Bit %v unexpectedly set", s-42)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user