diff --git a/index.html b/index.html index af6cffa..007b6f8 100644 --- a/index.html +++ b/index.html @@ -25,41 +25,57 @@ {{ block "update" .Update }}
{{ with .Content }}{{ . }}{{ end }}
{{ end }} - - {{ block "svg" .SVG }} + {{ block "treemap" .Treemap }}
- {{ . }} + {{- with .ViewBox }} + + {{- end }} + + + + + + + + {{- range .Regions }}{{ with .Rect }} + + {{- end }}{{ end }} + + {{- range .Regions }} + + {{.Prefix}}\n", p + + + {{- end }} +
- {{ end }} + {{- end }} {{ block "used" .Api }}
-

grouped

{{- range .UsedGrouped }} - {{ $first := index . 0 }} - {{ $bits := $first.Bits }} -
-

/{{ $bits }}

- - {{ range . }} - {{ end }} -
{{.Addr}}{{.Bits}}
-
- {{ end }} + {{- $first := index . 0 }} + {{- $bits := $first.Bits }} +
+

/{{ $bits }}

+ + {{- range . }} + + {{- end }} +
{{.Addr}}{{.Bits}}
+
+ {{ end -}}
- -

all

{{- range .Used }} - {{ end }} -
{{.Addr}}{{.Bits}}
+ {{.Addr}}{{.Bits}}{{ end }}
- {{ end }} + {{- end }} diff --git a/ipalloc/ipalloc.go b/ipalloc/ipalloc.go index 45ed0f2..3b81235 100644 --- a/ipalloc/ipalloc.go +++ b/ipalloc/ipalloc.go @@ -441,6 +441,93 @@ func (db *DB) Root() netip.Prefix { return db.provisions.prefix } +type Treemap struct { + ViewBox Rect + Regions []Region +} +type Region struct { + Prefix netip.Prefix + Rect Rect +} + +func (db *DB) Treemap() Treemap { + var ret Treemap + root := db.provisions.prefix + ret.ViewBox = prefixShape(root) + // leave room for shadows + ret.ViewBox.X-- + ret.ViewBox.Y-- + ret.ViewBox.W += 2 + ret.ViewBox.H += 2 + + for _, p := range db.Used() { + ret.Regions = append(ret.Regions, Region{Prefix: p, Rect: placeInto(p, root)}) + } + + return ret +} + +type Rect struct { + X, Y, W, H int +} + +func prefixShape(p netip.Prefix) Rect { + bits := p.Addr().BitLen() - p.Bits() + + return Rect{ + Y: 0, + X: 0, + H: 1 << (bits / 2), + W: 1 << ((bits + 1) / 2), + } +} + +func placeInto(p, into netip.Prefix) Rect { + return placeIntoRect(p, into, prefixShape(into)) +} +func placeIntoRect(p, into netip.Prefix, r Rect) Rect { + if p == into { + return r + } + if p.Bits() < into.Bits() { + panic("unreachable") + } + lo, hi := SplitPrefix(into) + + switch { + case lo.Overlaps(p): + var subr Rect + if r.W > r.H { + subr.Y = r.Y + subr.X = r.X + subr.H = r.H + subr.W = r.W / 2 + } else { + subr.Y = r.Y + subr.X = r.X + subr.H = r.H / 2 + subr.W = r.W + } + return placeIntoRect(p, lo, subr) + case hi.Overlaps(p): + var subr Rect + if r.W > r.H { + subr.Y = r.Y + subr.X = (r.X + (r.X + r.W)) / 2 + subr.H = r.H + subr.W = r.W / 2 + } else { + subr.Y = (r.Y + (r.Y + r.H)) / 2 + subr.X = r.X + subr.H = r.H / 2 + subr.W = r.W + } + return placeIntoRect(p, hi, subr) + default: + panic("unreachable") + } +} + //func (db *DB) Provision(p netip.Prefix) error { // db.Lock() // defer db.Unlock() diff --git a/main.go b/main.go index b145935..e30c408 100644 --- a/main.go +++ b/main.go @@ -49,9 +49,9 @@ func (s *server) apiPrefix(w http.ResponseWriter, r *http.Request) { } type PageContent struct { - Api *ipalloc.DB - Update UpdateMessage - SVG template.HTML + Api *ipalloc.DB + Update UpdateMessage + Treemap ipalloc.Treemap } type UpdateMessage struct { Type string @@ -59,7 +59,7 @@ type UpdateMessage struct { } func (s *server) template(w http.ResponseWriter, r *http.Request) { - err := s.tmpl.Execute(w, PageContent{Api: s.ipdb, SVG: renderSVG(s.ipdb.Used(), s.ipdb.Root())}) + err := s.tmpl.Execute(w, PageContent{Api: s.ipdb, Treemap: s.ipdb.Treemap()}) if err != nil { log.Println(err) } @@ -103,7 +103,7 @@ func (s *server) apiAlloc(w http.ResponseWriter, r *http.Request) { updmsg.Type = "hint" updmsg.Content = fmt.Sprintf("Added %s", alloc.Prefix()) } - s.tmpl.ExecuteTemplate(w, "svg", renderSVG(s.ipdb.Used(), s.ipdb.Root())) + s.tmpl.ExecuteTemplate(w, "treemap", s.ipdb.Treemap()) if err := s.tmpl.ExecuteTemplate(w, "update", updmsg); err != nil { log.Println(err) } @@ -135,7 +135,7 @@ func (s *server) apiDealloc(w http.ResponseWriter, r *http.Request) { updmsg.Type = "hint" updmsg.Content = fmt.Sprintf("Removed %s", prefix) } - s.tmpl.ExecuteTemplate(w, "svg", renderSVG(s.ipdb.Used(), s.ipdb.Root())) + s.tmpl.ExecuteTemplate(w, "treemap", s.ipdb.Treemap()) s.tmpl.ExecuteTemplate(w, "update", updmsg) s.tmpl.ExecuteTemplate(w, "used", s.ipdb) } @@ -159,91 +159,3 @@ func main() { s.setup() http.ListenAndServe("[::1]:8080", &s) } - -type rect struct { - y, x, h, w int -} - -func prefixShape(p netip.Prefix) rect { - bits := p.Addr().BitLen() - p.Bits() - - return rect{ - y: 0, - x: 0, - h: 1 << (bits / 2), - w: 1 << ((bits + 1) / 2), - } -} - -func placeInto(p, into netip.Prefix) rect { - return placeIntoRect(p, into, prefixShape(into)) -} -func placeIntoRect(p, into netip.Prefix, r rect) rect { - if p == into { - return r - } - if p.Bits() < into.Bits() { - panic("unreachable") - } - lo, hi := ipalloc.SplitPrefix(into) - - switch { - case lo.Overlaps(p): - var subr rect - if r.w > r.h { - subr.y = r.y - subr.x = r.x - subr.h = r.h - subr.w = r.w / 2 - } else { - subr.y = r.y - subr.x = r.x - subr.h = r.h / 2 - subr.w = r.w - } - return placeIntoRect(p, lo, subr) - case hi.Overlaps(p): - var subr rect - if r.w > r.h { - subr.y = r.y - subr.x = (r.x + (r.x + r.w)) / 2 - subr.h = r.h - subr.w = r.w / 2 - } else { - subr.y = (r.y + (r.y + r.h)) / 2 - subr.x = r.x - subr.h = r.h / 2 - subr.w = r.w - } - return placeIntoRect(p, hi, subr) - default: - panic("unreachable") - } -} -func renderSVG(ps []netip.Prefix, into netip.Prefix) template.HTML { - var b strings.Builder - shape := prefixShape(into) - fmt.Fprintf(&b, "\n", shape.w+2, shape.h+2) - fmt.Fprintf(&b, " ") - fmt.Fprintf(&b, " ") - fmt.Fprintf(&b, " ") - fmt.Fprintf(&b, " ") - fmt.Fprintf(&b, ``) - - for _, p := range ps { - place := placeInto(p, into) - fmt.Fprintf(&b, " \n") - fmt.Fprintf(&b, " \n", place.x, place.y, place.w, place.h) - fmt.Fprintf(&b, " \n") - } - for _, p := range ps { - place := placeInto(p, into) - fmt.Fprintf(&b, " \n") - fmt.Fprintf(&b, " %s\n", p) - fmt.Fprintf(&b, " \n", p, place.x, place.y, place.w, place.h) - fmt.Fprintf(&b, " \n") - } - - fmt.Fprintf(&b, ``) - return template.HTML(b.String()) -}