Draw groups: one reference draw per render, a drawGroup option, and a record's fences at load #16
@@ -709,7 +709,9 @@ tokens add cost in proportion to the output.
|
||||
category, which both reference. That settled, a record's column fences run at `New`
|
||||
too, so a category that loads renders as whichever shape is asked for, and a reference
|
||||
reaching back into a category through another one is refused there as the overlap it
|
||||
is.
|
||||
is. Which reads those fences weigh differs on purpose: a record-only template's inert
|
||||
format renders nothing, so a `drawGroup` on it can never matter and is refused, while
|
||||
one on a rendering format can matter to a caller that bare-references it.
|
||||
- **A record's column set is fixed before the first draw.** Only a category-level
|
||||
template is a record: a path descending into a field, or naming a folder or a
|
||||
choice, errors. A tail may pass through a choice whose variants carry different
|
||||
|
||||
@@ -154,8 +154,7 @@ func (c *drawCheck) checkRecordDraws(path string, n node) error {
|
||||
if !ok || !t.record {
|
||||
return nil
|
||||
}
|
||||
// A record's columns are its fields, which its format need not render at all, so the
|
||||
// reads to weigh are theirs rather than the template's own.
|
||||
// A record-only template's format renders nothing, so weigh the columns, not the format.
|
||||
columns := recordColumns(t)
|
||||
reads := false
|
||||
for _, name := range columns {
|
||||
|
||||
@@ -197,9 +197,6 @@ func checkRenders(s nodeScope) error {
|
||||
return err
|
||||
}
|
||||
fence := &drawCheck{}
|
||||
// One pass refuses a draw group that splits nothing and notes whether the scope binds a
|
||||
// reference at all: weighing what a render's draws hold walks every node it renders, and
|
||||
// with nothing bound there is no draw to share, so the shipped set pays for neither walk.
|
||||
refs := false
|
||||
if err := s(func(path string, n node) error {
|
||||
if t, ok := n.(*template); ok && len(t.refs) > 0 {
|
||||
|
||||
+2
-5
@@ -51,11 +51,7 @@ func TestNoRenderAllocRegression(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A record's fences read the compiled tree, so they belong to New, not to a draw.
|
||||
// A reference path read across a repeat, and one read in a named draw group. The repeat
|
||||
// prices both measures that keep a render's draw set off the heap — inlining expandAnew
|
||||
// into render's loop, or keying a draw group's map with the scope's own string, costs an
|
||||
// allocation an iteration — while the draw group holds the group path's own count.
|
||||
// The repeat shape prices both escape measures: dropping either costs an alloc an iteration.
|
||||
func TestNoReferenceAllocRegression(t *testing.T) {
|
||||
word := `{"format":"{w}","w":["alpha","beta","gamma","delta"]}`
|
||||
for _, s := range []struct {
|
||||
@@ -78,6 +74,7 @@ func TestNoReferenceAllocRegression(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A record's fences read the compiled tree, so they belong to New, not to a draw.
|
||||
func TestNoRecordAllocRegression(t *testing.T) {
|
||||
for _, s := range []struct{ name, json string }{
|
||||
{"record 3 columns", `{"format":"","a":"x","b":"y","c":"z"}`},
|
||||
|
||||
Reference in New Issue
Block a user