mirror of
https://github.com/soypat/lneto.git
synced 2026-08-04 15:07:52 +00:00
124 lines
2.9 KiB
Go
124 lines
2.9 KiB
Go
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
|
|
}
|