cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Engine: borders packed in a byte, so a cell stays 256 bytes

Dami committed 3fd4dc9
Four lines of two bits each keep Style at eight bytes; with four bytes
of borders a Cell grew to 264 bytes and into the 288-byte size class,
which cost every formula cell and every entry typed 32 bytes more.
commit
3fd4dc903960cee7be21f4ffbe031f88efe4a8d6
parent
a18133cab31326de2b142dd781ab32c5b89910bd
author
Dami <dami.etoile@gmail.com>
kind
refactor risk score 2.4 of 3, Jev's estimate
9 files changed, +69 -57
Minternal/fileio/xlsx_excelize_test.go11+-----
Minternal/fileio/xlsxlayout.go20+++++------
Minternal/fileio/xlsxlayout_test.go4+-
Minternal/fileio/xlsxwrite.go2+-
Minternal/sheet/borders.go52++++++++++++++++++++++--------
Minternal/sheet/cellfile.go13+++-----
Minternal/sheet/layout_test.go14++++----
Minternal/sheet/rowlayout.go8++--
Minternal/stress/gen.go2+-
M internal/fileio/xlsx_excelize_test.go +2 -9
M internal/fileio/xlsxlayout.go +9 -11
M internal/fileio/xlsxlayout_test.go +2 -2
M internal/fileio/xlsxwrite.go +1 -1
M internal/sheet/borders.go +38 -14
@@ -41,13 +41,40 @@ func ParseLine(s string) (Line, bool) {
4141 // thin.4242 func Heavier(a, b Line) Line { return max(a, b) }4343 44-// Borders are the lines on a cell's four edges.45-type Borders struct {46- Top, Bottom, Left, Right Line44+// Borders are the lines on a cell's four edges, two bits each, so a45+// style stays eight bytes and a cell fits its allocation size class.46+type Borders uint847+48+// Edge is one of a cell's edges.49+type Edge uint850+51+const (52+ EdgeTop Edge = iota53+ EdgeBottom54+ EdgeLeft55+ EdgeRight56+)57+58+// BordersOf is the borders with these lines.59+func BordersOf(top, bottom, left, right Line) Borders {60+ return Borders(0).With(EdgeTop, top).With(EdgeBottom, bottom).With(EdgeLeft, left).With(EdgeRight, right)4761 }4862 63+// Line is the line on edge e.64+func (b Borders) Line(e Edge) Line { return Line(b >> (2 * e) & 3) }65+66+// With is b with edge e drawn with l.67+func (b Borders) With(e Edge, l Line) Borders {68+ return b&^(3<<(2*e)) | Borders(l&3)<<(2*e)69+}70+71+func (b Borders) Top() Line { return b.Line(EdgeTop) }72+func (b Borders) Bottom() Line { return b.Line(EdgeBottom) }73+func (b Borders) Left() Line { return b.Line(EdgeLeft) }74+func (b Borders) Right() Line { return b.Line(EdgeRight) }75+4976 // IsZero reports whether no edge has a line.50-func (b Borders) IsZero() bool { return b == Borders{} }77+func (b Borders) IsZero() bool { return b == 0 }5178 5279 // BorderKind is which edges of a range Format > Borders sets.5380 type BorderKind uint8
@@ -117,12 +144,9 @@ func (k BorderKind) edges(r Rect, a Addr) edgeSet {
117144 118145 // apply sets the edges of e on b to l.119146 func (e edgeSet) apply(b *Borders, l Line) {120- for _, on := range [...]struct {121- set bool122- edge *Line123- }{{e.top, &b.Top}, {e.bottom, &b.Bottom}, {e.left, &b.Left}, {e.right, &b.Right}} {124- if on.set {125- *on.edge = l147+ for edge, set := range [...]bool{EdgeTop: e.top, EdgeBottom: e.bottom, EdgeLeft: e.left, EdgeRight: e.right} {148+ if set {149+ *b = b.With(Edge(edge), l)126150 }127151 }128152 }
@@ -173,9 +197,9 @@ func (s *Sheet) clearFacing(r Rect, k BorderKind) {
173197 // EdgeAbove is the line on the edge between the cell at a and the one174198 // above it: the heavier of their facing lines.175199 func (s *Sheet) EdgeAbove(a Addr) Line {176- l := s.CellStyle(a).Borders.Top200+ l := s.CellStyle(a).Borders.Top()177201 if a.Row > 0 {178- l = Heavier(l, s.CellStyle(Addr{Col: a.Col, Row: a.Row - 1}).Borders.Bottom)202+ l = Heavier(l, s.CellStyle(Addr{Col: a.Col, Row: a.Row - 1}).Borders.Bottom())179203 }180204 return l181205 }
@@ -183,9 +207,9 @@ func (s *Sheet) EdgeAbove(a Addr) Line {
183207 // EdgeLeft is the line on the edge between the cell at a and the one to184208 // its left.185209 func (s *Sheet) EdgeLeft(a Addr) Line {186- l := s.CellStyle(a).Borders.Left210+ l := s.CellStyle(a).Borders.Left()187211 if a.Col > 0 {188- l = Heavier(l, s.CellStyle(Addr{Col: a.Col - 1, Row: a.Row}).Borders.Right)212+ l = Heavier(l, s.CellStyle(Addr{Col: a.Col - 1, Row: a.Row}).Borders.Right())189213 }190214 return l191215 }
M internal/sheet/cellfile.go +5 -8
M internal/sheet/layout_test.go +7 -7
@@ -28,10 +28,10 @@ func TestBorders(t *testing.T) {
2828 cell string2929 want Borders3030 }{31- {"B2", Borders{Top: LineThin, Left: LineThin}},32- {"C2", Borders{Top: LineThin, Right: LineThin}},33- {"B3", Borders{Bottom: LineThin, Left: LineThin}},34- {"C3", Borders{Bottom: LineThin, Right: LineThin}},31+ {"B2", BordersOf(LineThin, LineNone, LineThin, LineNone)},32+ {"C2", BordersOf(LineThin, LineNone, LineNone, LineThin)},33+ {"B3", BordersOf(LineNone, LineThin, LineThin, LineNone)},34+ {"C3", BordersOf(LineNone, LineThin, LineNone, LineThin)},3535 } {3636 if got := s.CellStyle(at(c.cell)).Borders; got != c.want {3737 t.Errorf("outer: %s = %+v, want %+v", c.cell, got, c.want)
@@ -44,12 +44,12 @@ func TestBorders(t *testing.T) {
4444 // Inner draws between the cells only; a later thick top clears the4545 // neighbor's facing bottom, so the last change shows.4646 s.SetBorders(rng("B2:C3"), BorderInner, LineDouble)47- if got := s.CellStyle(at("B2")).Borders; got != (Borders{Top: LineThin, Left: LineThin, Bottom: LineDouble, Right: LineDouble}) {47+ if got := s.CellStyle(at("B2")).Borders; got != BordersOf(LineThin, LineDouble, LineThin, LineDouble) {4848 t.Errorf("inner B2 = %+v", got)4949 }5050 s.SetBorders(rng("B1"), BorderBottom, LineThin)5151 s.SetBorders(rng("B2"), BorderTop, LineThick)52- if got := s.CellStyle(at("B1")).Borders.Bottom; got != LineNone {52+ if got := s.CellStyle(at("B1")).Borders.Bottom(); got != LineNone {5353 t.Errorf("facing bottom kept: %v", got)5454 }5555 if s.EdgeAbove(at("B2")) != LineThick {
@@ -66,7 +66,7 @@ func TestBorders(t *testing.T) {
6666 t.Errorf("formatting-only cells left: %d", s.cells.len())6767 }6868 s.Undo()69- if s.CellStyle(at("C3")).Borders.Right != LineThin {69+ if s.CellStyle(at("C3")).Borders.Right() != LineThin {7070 t.Error("undo none")7171 }7272 // Whole columns keep borders on the column.
M internal/sheet/rowlayout.go +4 -4
@@ -74,7 +74,7 @@ func (s *Sheet) linesShape() bool {
7474 }7575 7676 // horizontal reports whether b draws its top or bottom edge.77-func (b Borders) horizontal() bool { return b.Top != LineNone || b.Bottom != LineNone }77+func (b Borders) horizontal() bool { return b.Top() != LineNone || b.Bottom() != LineNone }7878 7979 // Shaped reports whether any row may be other than one line of text8080 // under the one above: something wraps or draws borders, a row has a
@@ -154,16 +154,16 @@ func (s *Sheet) WrappedIn(row int) []int {
154154 // column's, row's or the sheet's format drawing either.155155 func (s *Sheet) RuleAbove(row int) bool {156156 if s.linesShape() && (s.shapers.colRules || s.lines.sheet.Style.Borders.horizontal() ||157- s.lines.rows[row].Style.Borders.Top != LineNone || row > 0 && s.lines.rows[row-1].Style.Borders.Bottom != LineNone) {157+ s.lines.rows[row].Style.Borders.Top() != LineNone || row > 0 && s.lines.rows[row-1].Style.Borders.Bottom() != LineNone) {158158 return true159159 }160160 found := false161161 s.shapersIn(row, func(c int) {162- found = found || s.CellStyle(Addr{Col: c, Row: row}).Borders.Top != LineNone162+ found = found || s.CellStyle(Addr{Col: c, Row: row}).Borders.Top() != LineNone163163 })164164 if row > 0 && !found {165165 s.shapersIn(row-1, func(c int) {166- found = found || s.CellStyle(Addr{Col: c, Row: row - 1}).Borders.Bottom != LineNone166+ found = found || s.CellStyle(Addr{Col: c, Row: row - 1}).Borders.Bottom() != LineNone167167 })168168 }169169 return found
M internal/stress/gen.go +1 -1