-// load keeps the node of line when it is built, and tells if it is.
-func (v *View) load(tree ViewNodeReader, line NodeKey) (bool, error) {
- node, built, err := tree.Node(line.level, line.index)
- if err != nil || !built {
- return false, err
- }
-
- v.nodes[line] = node
-
- return true, nil
-}
-
-// fit merges the most due pairs while a batch runs, and tells if a merge
-// changed the view.
-func (v *View) fit(tree ViewNodeReader) (bool, error) {
- size := v.Size()
- if size > v.high {
- v.merging = true
- }
-
- merged := false
- for v.merging && size > v.low {
- best, err := v.mostDue(tree)
- if err != nil || best < 0 {
- return merged, err
- }
-
- left, right := v.lines[best], v.lines[best+1]
- parent := NodeKey{left.level + 1, left.index / 2}
- _, err = v.load(tree, parent)
- if err != nil {
- return merged, err
- }
-
- size += v.lineSize(parent) - v.lineSize(left) - v.lineSize(right)
- delete(v.nodes, left)
- delete(v.nodes, right)
-
- v.lines[best] = parent
- v.lines = append(v.lines[:best+1], v.lines[best+2:]...)
- merged = true
- }
-
- v.merging = false
-
- return merged, nil
-}
-
-// renderLine writes one view line, "id+n|text", and a newline.
-func (v *View) renderLine(text *strings.Builder, line NodeKey) {
- body := viewPending
- node, built := v.nodes[line]
- if built {
- body = strings.ReplaceAll(node.Text, "\n", " ")
- }
-
- text.WriteString(strconv.Itoa(line.index << line.level))
- text.WriteByte('+')
- text.WriteString(strconv.Itoa(1 << line.level))
- text.WriteByte('|')
- text.WriteString(body)
- text.WriteByte('\n')
-}
-
-// Size returns the sum of the line sizes.
-func (v *View) Size() int {
- size := 0
- for _, line := range v.lines {
- size += v.lineSize(line)
- }
-
- return size
-}
-
-// mostDue returns the position of the left line of the most due sibling pair
-// whose parent is built, or -1 when there is no such pair.
-func (v *View) mostDue(tree ViewNodeReader) (int, error) {
- best := -1
- bestDue := 0.0
- for n := 0; n+1 < len(v.lines); n++ {
- left, right := v.lines[n], v.lines[n+1]
- if left.index%2 != 0 || right.level != left.level || right.index != left.index+1 {
- continue
- }
-
- last := nodeEnd(right.level, right.index) - 1
- due := math.Ldexp(float64(v.end-last), -left.level)
- if best >= 0 && due <= bestDue {
- continue
+// restoreView returns the view of state with the limits high and low. Each line
+// must continue the one before it, and each line above level 0 must be built.
+func restoreView(state ViewState, high, low int, tree ViewNodeReader) (*View, error) {
+ v := newView(high, low)
+ v.merging = state.Merging
+ for _, pair := range state.Lines {
+ line := NodeKey{pair[0], pair[1]}
+ if line.level < 0 || line.level >= bits.UintSize-2 || line.index < 0 || line.index<<line.level != v.end {
+ return nil, fmt.Errorf("view line (%d, %d) does not start at message %d", line.level, line.index, v.end)