mirror of
https://github.com/soypat/lneto.git
synced 2026-08-21 15:09:05 +00:00
rethinking ring buffer semantics and breaking everything in the process
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ZonePrinter struct {
|
||||
zbuf []BufferZone
|
||||
aux []byte
|
||||
}
|
||||
|
||||
type BufferZone struct {
|
||||
Name string
|
||||
Start, End int
|
||||
}
|
||||
|
||||
func (bp *ZonePrinter) AppendPrintZones(dst []byte, bufSize int, zones ...BufferZone) ([]byte, error) {
|
||||
n := bufSize
|
||||
if n == 0 {
|
||||
return dst, errors.New("empty buffer")
|
||||
}
|
||||
if len(bp.zbuf) != 0 {
|
||||
panic("race condition detected: zbuf not zero lengthed- ZonePrinter not to be used concurrently")
|
||||
}
|
||||
// Zeroed zone buffer. Will be used to detect collisions.
|
||||
// TODO(soypat): Really wasteful, there's an obvious way to just have len(zones)+1 zbuf, but I've already spent too much time here. This also makes collision detection really easy so whatever.
|
||||
bp.zbuf = append(bp.zbuf[:0], make([]BufferZone, n)...)
|
||||
labels := bp.zbuf
|
||||
|
||||
// helper to paint an interval on the ring
|
||||
paint := func(z BufferZone) error {
|
||||
if z.End == 0 { // 0 == empty
|
||||
return nil
|
||||
}
|
||||
if z.Start < z.End {
|
||||
for i := z.Start; i < z.End; i++ {
|
||||
if labels[i].Name != "" {
|
||||
return fmt.Errorf("paint collision: %q/%q @%d size=%d", labels[i].Name, z.Name, i, bufSize)
|
||||
}
|
||||
labels[i].Name = z.Name
|
||||
}
|
||||
} else { // wrap
|
||||
for i := z.Start; i < n; i++ {
|
||||
if labels[i].Name != "" {
|
||||
return fmt.Errorf("wrap(start) paint collision: %q/%q @%d size=%d", labels[i].Name, z.Name, i, bufSize)
|
||||
}
|
||||
labels[i].Name = z.Name
|
||||
}
|
||||
for i := 0; i < z.End; i++ {
|
||||
if labels[i].Name != "" {
|
||||
return fmt.Errorf("wrap(end) paint collision: %q/%q @%d size=%d", labels[i].Name, z.Name, i, bufSize)
|
||||
}
|
||||
labels[i].Name = z.Name
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// paint
|
||||
for _, zone := range zones {
|
||||
err := paint(zone)
|
||||
if err != nil {
|
||||
return dst, err
|
||||
}
|
||||
}
|
||||
|
||||
// unpainted sections are free.
|
||||
for i := range labels {
|
||||
if labels[i].Name == "" {
|
||||
labels[i].Name = "free"
|
||||
}
|
||||
}
|
||||
// Compress to segments.
|
||||
icurrent := 0
|
||||
for i := 1; i < n; i++ {
|
||||
current := labels[icurrent]
|
||||
next := labels[i]
|
||||
if current.Name != next.Name {
|
||||
labels[icurrent].End = i
|
||||
icurrent++
|
||||
labels[icurrent].Start = i
|
||||
labels[icurrent].Name = next.Name
|
||||
}
|
||||
}
|
||||
segs := labels[:icurrent+1]
|
||||
segs[icurrent].End = n
|
||||
|
||||
var l1 []byte = dst
|
||||
var l2 []byte = bp.aux[:0]
|
||||
for _, s := range segs {
|
||||
l2start := len(l2)
|
||||
if s.Name == "free" {
|
||||
l2 = append(l2, "| "...)
|
||||
} else {
|
||||
l2 = append(l2, "|--"...)
|
||||
}
|
||||
l2 = append(l2, s.Name...)
|
||||
l2 = append(l2, '(')
|
||||
l2 = strconv.AppendInt(l2, int64(s.End-s.Start), 10)
|
||||
if s.Name == "free" {
|
||||
l2 = append(l2, ") "...)
|
||||
} else {
|
||||
l2 = append(l2, ")--"...)
|
||||
}
|
||||
fraglen := len(l2) - l2start
|
||||
|
||||
l1start := len(l1)
|
||||
l1 = strconv.AppendInt(l1, int64(s.Start), 10)
|
||||
|
||||
paddingNeeded := fraglen - (len(l1) - l1start)
|
||||
for range paddingNeeded {
|
||||
l1 = append(l1, ' ')
|
||||
}
|
||||
}
|
||||
l1 = strconv.AppendInt(l1, int64(bufSize), 10)
|
||||
l1 = append(l1, '\n')
|
||||
l2 = append(l2, '|')
|
||||
l1 = append(l1, l2...)
|
||||
bp.zbuf = bp.zbuf[:0] // zero out, used to detect concurrent usage of ZonePrinter.
|
||||
return l1, nil
|
||||
}
|
||||
Reference in New Issue
Block a user