span buffers: Simplified code by throwing away cases that cannot happen. Extended unit tests with more pattern to check.

This commit is contained in:
Sascha L. Teichmann 2014-10-21 18:03:27 +02:00
parent 269e63ea59
commit 4f49d114c6
2 changed files with 74 additions and 64 deletions

View File

@ -90,65 +90,33 @@ func (sp *SpanPool) Insert(s *Span, pos, value int32) *Span {
} }
head := s head := s
for prev := s; s != nil; s = s.Next { for ; s != nil && pos > s.To; s = s.Next {
switch { next := s.Next
case pos < s.From: // before span if pos == s.To+1 && value == s.Value { // directly neighbored
if pos == s.From-1 && value == s.Value { // directly neighbored s.To = pos
s.From = pos // Check if a gap has to be closed
// Check if a gap has to be closed. if next != nil && next.From == s.To+1 && value == next.Value {
if prev.To == s.From-1 && value == prev.Value { s.To = next.To
prev.To = s.To s.Next = next.Next
prev.Next = s.Next sp.Free(next)
sp.Free(s)
return head
}
} }
// Extend prev? return head
if prev.To == pos-1 && value == prev.Value { }
prev.To = pos // Extend next?
return head if next != nil && pos == next.From-1 && value == next.Value {
} next.From = pos
// New between prev and current. return head
}
// Before next -> New between current and next
if next == nil || pos < next.From {
sn := sp.Alloc() sn := sp.Alloc()
sn.From = pos sn.From = pos
sn.To = pos sn.To = pos
sn.Value = value sn.Value = value
sn.Next = s sn.Next = next
prev.Next = sn s.Next = sn
return head
case pos > s.To: // after span
next := s.Next
if pos == s.To+1 && value == s.Value { // directly neighbored
s.To = pos
// Check if a gap has to be closed
if next != nil && next.From == s.To+1 && value == next.Value {
s.To = next.To
s.Next = next.Next
sp.Free(next)
}
return head
}
// Extend next?
if next != nil && pos == next.From-1 && value == next.Value {
next.From = pos
return head
}
// Before next -> New between current and next
if next == nil || pos < next.From {
sn := sp.Alloc()
sn.From = pos
sn.To = pos
sn.Value = value
sn.Next = next
s.Next = sn
return head
}
default: // pos in span -> do not modify.
return head return head
} }
prev = s
} }
return head return head

View File

@ -9,26 +9,68 @@ import (
"testing" "testing"
) )
func TestSpans(t *testing.T) { const spanItems = 3000
inp := make([]int32, 3000) func TestSpans(t *testing.T) {
for i, n := 0, len(inp); i < n; i++ {
inp[i] = int32(i)
}
for i, n := 0, len(inp); i < n; i++ {
i1 := rand.Int31n(int32(n))
i2 := rand.Int31n(int32(n))
inp[i1], inp[i2] = inp[i2], inp[i1]
}
sp := NewSpanPool() sp := NewSpanPool()
var s *Span var s *Span
for i, n := 0, len(inp); i < n; i++ {
for i := 0; i < spanItems; i++ {
s = sp.Insert(s, int32(i), 42)
}
if n := s.Len(); n != 1 {
t.Errorf("inc: Span length %d expected 1\n", n)
t.Errorf("spans: %s\n", s)
}
sp.FreeAll(s)
s = nil
for i := spanItems - 1; i >= 0; i-- {
s = sp.Insert(s, int32(i), 42)
}
if n := s.Len(); n != 1 {
t.Errorf("dec: Span length %d expected 1\n", n)
t.Errorf("spans: %s\n", s)
}
sp.FreeAll(s)
s = nil
for i := 0; i < spanItems/2; i++ {
j := spanItems - 1 - i
s = sp.Insert(s, int32(i), 42)
s = sp.Insert(s, int32(j), 21)
}
if n := s.Len(); n != 2 {
t.Errorf("two: Span length %d expected 2\n", n)
t.Errorf("spans: %s\n", s)
}
sp.FreeAll(s)
inp := make([]int32, spanItems)
for i := 0; i < spanItems; i++ {
inp[i] = int32(i)
}
for i := 0; i < spanItems; i++ {
i1 := rand.Int31n(int32(spanItems))
i2 := rand.Int31n(int32(spanItems))
inp[i1], inp[i2] = inp[i2], inp[i1]
}
s = nil
for i := 0; i < spanItems; i++ {
s = sp.Insert(s, inp[i], 42) s = sp.Insert(s, inp[i], 42)
} }
if n := s.Len(); n != 1 { if n := s.Len(); n != 1 {
t.Errorf("Span length %d expected 1\n", n) t.Errorf("rand: Span length %d expected 1\n", n)
t.Errorf("spans: %s\n", s) t.Errorf("spans: %s\n", s)
} }