The string table keeps a running total of the heap its strings hold, so neither UndoCost, which the UI asks before every command that edits, nor the store's size walks the strings; the rich cells in the range are found through whichever of the range and the side table is smaller.
- commit
- 652244f4165f28435486fba1ad48e73f16bc88f5
- parent
- c1c3881977a881f32f9c2ab156747db68b2c3597
- author
- Dami <dami.etoile@gmail.com>
- kind
- refactor risk score 1.6 of 3, Jev's estimate
| M | internal/ | 10 | ++++++++-- |
| M | internal/ | 22 | ++++++++++++++-------- |
| M | internal/ | 8 | ++++---- |
@@ -133,10 +133,16 @@ func TestUndoPlainCellsAsSlots(t *testing.T) {
133133 s.Load(Addr{Row: r}, fmt.Sprintf("%d.25", r), Format{}, Style{})134134 }135135 s.RecalcAll()136- s.EraseRange(NewRect(Addr{}, Addr{Row: n - 1}))137- if got := s.Book().HistoryBytes(); got > n*(slotBytes+4) {136+ r := NewRect(Addr{}, Addr{Row: n - 1})137+ cost := s.UndoCost(r)138+ s.EraseRange(r)139+ got := s.Book().HistoryBytes()140+ if got > n*(slotBytes+4) {138141 t.Errorf("clearing %d numbers is estimated at %d bytes", n, got)139142 }143+ if cost < got*9/10 || cost > got*11/10 {144+ t.Errorf("clearing %d numbers was to cost %d bytes, and took %d", n, cost, got)145+ }140146 }141147 142148 // A change too large to undo runs without recording, forgets the history@@ -130,7 +130,8 @@ const MaxStepBytes = 1 << 30
130130 131131 // UndoCost estimates the undo history a change to every cell of r would132132 // hold: a slot a cell, the rich cells in r whole, and r's share of the133-// sheet's strings.133+// sheet's strings. The UI asks it before every command that edits, so134+// it costs the fewer of r's cells and the sheet's rich cells.134135 func (s *Sheet) UndoCost(r Rect) int64 {135136 st := &s.cells136137 cells := 0@@ -140,17 +141,22 @@ func (s *Sheet) UndoCost(r Rect) int64 {
140141 if cells == 0 {141142 return 0142143 }143- n := int64(cells) * slotBytes144+ n := int64(cells)*slotBytes + st.strs.bytes*int64(cells)/int64(st.len())145+ add := func(c *Cell) { n += richBytes + cellSize(c) }146+ if cells < len(st.rich)-len(st.richFree) { // the cheaper way to find r's rich cells147+ for a := range st.anyKeysIn(r) {148+ if c := st.richAt(a); c != nil {149+ add(c)150+ }151+ }152+ return n153+ }144154 for _, rc := range st.rich {145155 if rc.c != nil && r.Contains(rc.a) {146- n += richBytes + cellSize(rc.c)156+ add(rc.c)147157 }148158 }149- var strs int64150- for _, str := range st.strs.strs {151- strs += strBytes + int64(len(str))152- }153- return n + strs*int64(cells)/int64(st.len())159+ return n154160 }155161 156162 // WithoutUndo runs fn, a change too large to undo (see MaxStepBytes),@@ -56,6 +56,7 @@ type strTable struct {
5656 refs []uint325757 index map[string]uint325858 free []uint3259+ bytes int64 // the estimated heap the strings hold, strBytes and their text each5960 }6061 6162 // add counts one more use of s and returns its entry.@@ -77,6 +78,7 @@ func (t *strTable) add(s string) uint32 {
7778 t.strs, t.refs = append(t.strs, s), append(t.refs, 1)7879 }7980 t.index[s] = i81+ t.bytes += strBytes + int64(len(s))8082 return i8183 }8284 @@ -86,6 +88,7 @@ func (t *strTable) release(i uint32) {
8688 return8789 }8890 if t.refs[i]--; t.refs[i] == 0 {91+ t.bytes -= strBytes + int64(len(t.strs[i]))8992 delete(t.index, t.strs[i])9093 t.strs[i] = ""9194 t.free = append(t.free, i)@@ -316,10 +319,7 @@ func (st *cellStore) size() int64 {
316319 for _, rc := range st.rich {317320 n += cellSize(rc.c)318321 }319- for _, s := range st.strs.strs {320- n += strBytes + int64(len(s))321- }322- return n322+ return n + st.strs.bytes323323 }324324 325325 // richCell is a rich cell and where it is.