Skip to content

Commit

Permalink
fix(cellbuf): accurately calculate lines hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Dec 22, 2024
1 parent d3b059e commit 0d34f0e
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 63 deletions.
44 changes: 19 additions & 25 deletions cellbuf/hardscroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@ import (
func (s *Screen) scrollOptimize() {
height := s.newbuf.Height()
if s.oldnum == nil || len(s.oldnum) < height {
var needLines int
if len(s.oldnum) < height {
needLines = height
} else {
needLines = len(s.oldnum)
}

s.oldnum = make([]int, needLines)
s.oldnum = make([]int, height)
}

// Calculate the indices
Expand Down Expand Up @@ -76,6 +69,11 @@ func (s *Screen) scrollOptimize() {

// scrolln scrolls the screen up by n lines.
func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) {
const (
nonDestScrollRegion = false
memoryBelow = false
)

blank := s.clearBlank()
if n > 0 {
// Scroll up (forward)
Expand All @@ -95,14 +93,15 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) {
}

// Clear newly shifted-in lines.
if v {
if v &&
(nonDestScrollRegion || (memoryBelow && bot == maxY)) {
if bot == maxY {
s.move(0, bot-n+1)
s.clearToBottom(&BlankCell)
s.clearToBottom(nil)
} else {
for i := 0; i < n; i++ {
s.move(0, bot-i)
s.clearToEnd(&BlankCell, false)
s.clearToEnd(nil, false)
}
}
}
Expand All @@ -123,10 +122,11 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) {
}

// Clear newly shifted-in lines.
if v {
if v &&
(nonDestScrollRegion || (memoryBelow && top == 0)) {
for i := 0; i < -n; i++ {
s.move(0, top+i)
s.clearToEnd(&BlankCell, false)
s.clearToEnd(nil, false)
}
}
}
Expand Down Expand Up @@ -154,21 +154,21 @@ func (s *Screen) scrollBuffer(b *Buffer, n, top, bot int, blank *Cell) {
if n < 0 {
// shift n lines downwards
limit := top - n
for line := bot; line >= limit && n >= 0 && n >= top; line-- {
for line := bot; line >= limit && line >= 0 && line >= top; line-- {
copy(b.Lines[line], b.Lines[line+n])
}
for line := top; line < limit && n <= b.Height()-1 && n <= bot; line++ {
for line := top; line < limit && line <= b.Height()-1 && line <= bot; line++ {
b.FillRect(blank, Rect(0, line, b.Width(), 1))
}
}

if n > 0 {
// shift n lines upwards
limit := bot - n
for line := top; line <= limit && n <= b.Height()-1 && n <= bot; line++ {
for line := top; line <= limit && line <= b.Height()-1 && line <= bot; line++ {
copy(b.Lines[line], b.Lines[line+n])
}
for line := bot; line > limit && n >= 0 && n >= top; line-- {
for line := bot; line > limit && line >= 0 && line >= top; line-- {
b.FillRect(blank, Rect(0, line, b.Width(), 1))
}
}
Expand All @@ -183,17 +183,11 @@ func (s *Screen) touchLine(width, height, y, n int, changed bool) {
}

for i := y; i < y+n && i < height; i++ {
chg, ok := s.touch[i]
if changed {
chg.firstCell = 0
chg.lastCell = width - 1
s.touch[i] = lineData{firstCell: 0, lastCell: width - 1}
} else {
if ok {
chg.firstCell = newIndex
chg.lastCell = newIndex
}
delete(s.touch, i)
}
s.touch[i] = chg
}
}

Expand Down
30 changes: 14 additions & 16 deletions cellbuf/hashmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,21 @@ const newIndex = -1
// updateHashmap updates the hashmap with the new hash value.
func (s *Screen) updateHashmap() {
height := s.newbuf.Height()
if height > len(s.hashtab) {
s.hashtab = make([]hashmap, height*2)
}

if s.oldhash != nil && s.newhash != nil {
if len(s.oldhash) >= height && len(s.newhash) >= height {
// rehash changed lines
for i := 0; i < height; i++ {
_, ok := s.touch[i]
if ok {
s.oldhash[i] = hash(s.curbuf.Line(i))
s.newhash[i] = hash(s.newbuf.Line(i))
}
}
} else {
// rehash all
if s.oldhash == nil {
if len(s.oldhash) != height {
s.oldhash = make([]uint64, height)
}
if s.newhash == nil {
if len(s.newhash) != height {
s.newhash = make([]uint64, height)
}
for i := 0; i < height; i++ {
Expand Down Expand Up @@ -94,7 +91,8 @@ func (s *Screen) updateHashmap() {
}

// Mark line pair corresponding to unique hash pairs.
for _, hsp := range s.hashtab {
for i := 0; i < len(s.hashtab) && s.hashtab[i].value != 0; i++ {
hsp := &s.hashtab[i]
if hsp.oldcount == 1 && hsp.newcount == 1 && hsp.oldindex != hsp.newindex {
s.oldnum[hsp.newindex] = hsp.oldindex
}
Expand All @@ -116,7 +114,7 @@ func (s *Screen) updateHashmap() {
start = i
shift = s.oldnum[i] - i
i++
for i < height && i < len(s.oldnum) && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
i++
}
size = i - start
Expand All @@ -134,7 +132,7 @@ func (s *Screen) updateHashmap() {

// scrollOldhash
func (s *Screen) scrollOldhash(n, top, bot int) {
if s.oldhash == nil {
if len(s.oldhash) == 0 {
return
}

Expand Down Expand Up @@ -165,7 +163,7 @@ func (s *Screen) growHunks() {
)

height := s.newbuf.Height()
for i < height && i < len(s.oldnum) && s.oldnum[i] == newIndex {
for i < height && s.oldnum[i] == newIndex {
i++
}
for ; i < height; i = nextHunk {
Expand All @@ -179,20 +177,20 @@ func (s *Screen) growHunks() {

// get forward limit
i = start + 1
for i < height && i < len(s.oldnum) &&
for i < height &&
s.oldnum[i] != newIndex &&
s.oldnum[i]-i == shift {
i++
}

end = i
for i < height && i < len(s.oldnum) && s.oldnum[i] == newIndex {
for i < height && s.oldnum[i] == newIndex {
i++
}

nextHunk = i
forwardLimit = i
if i >= height || (i < len(s.oldnum) && s.oldnum[i] >= i) {
if i >= height || s.oldnum[i] >= i {
forwardRefLimit = i
} else {
forwardRefLimit = s.oldnum[i]
Expand Down Expand Up @@ -284,7 +282,7 @@ func (s *Screen) costEffective(from, to int, blank bool) bool {

func (s *Screen) updateCost(from, to Line) (cost int) {
var fidx, tidx int
for i := s.newbuf.Width(); i > 0; i, fidx, tidx = i-1, fidx+1, tidx+1 {
for i := s.newbuf.Width() - 1; i > 0; i, fidx, tidx = i-1, fidx+1, tidx+1 {
if !cellEqual(from.At(fidx), to.At(tidx)) {
cost++
}
Expand All @@ -294,7 +292,7 @@ func (s *Screen) updateCost(from, to Line) (cost int) {

func (s *Screen) updateCostBlank(to Line) (cost int) {
var tidx int
for i := s.newbuf.Width(); i > 0; i, tidx = i-1, tidx+1 {
for i := s.newbuf.Width() - 1; i > 0; i, tidx = i-1, tidx+1 {
if !cellEqual(nil, to.At(tidx)) {
cost++
}
Expand Down
43 changes: 21 additions & 22 deletions cellbuf/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,29 +332,27 @@ func (s *Screen) Clear() bool {

// ClearRect implements Window.
func (s *Screen) ClearRect(r Rectangle) bool {
s.newbuf.ClearRect(r)
s.mu.Lock()
for i := r.Min.Y; i < r.Max.Y; i++ {
s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X - 1}
}
s.mu.Unlock()
return true
return s.FillRect(nil, r)
}

// Draw implements Window.
func (s *Screen) Draw(x int, y int, cell *Cell) (v bool) {
s.mu.Lock()
defer s.mu.Unlock()
cellWidth := 1
if cell != nil {
cellWidth = cell.Width
}
if prev := s.curbuf.Cell(x, y); !cellEqual(prev, cell) {
chg, ok := s.touch[y]
if !ok {
chg = lineData{firstCell: x, lastCell: x}
chg = lineData{firstCell: x, lastCell: x + cellWidth}
} else {
chg.firstCell = min(chg.firstCell, x)
chg.lastCell = max(chg.lastCell, x)
chg.lastCell = max(chg.lastCell, x+cellWidth)
}
s.touch[y] = chg
}
s.mu.Unlock()

return s.newbuf.Draw(x, y, cell)
}
Expand All @@ -366,12 +364,12 @@ func (s *Screen) Fill(cell *Cell) bool {

// FillRect implements Window.
func (s *Screen) FillRect(cell *Cell, r Rectangle) bool {
s.newbuf.FillRect(cell, r)
s.mu.Lock()
defer s.mu.Unlock()
s.newbuf.FillRect(cell, r)
for i := r.Min.Y; i < r.Max.Y; i++ {
s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X - 1}
s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X}
}
s.mu.Unlock()
return true
}

Expand Down Expand Up @@ -893,7 +891,7 @@ func (s *Screen) transformLine(y int) {
}

// Update the old line with the new line
if s.newbuf.Width() > firstCell && len(oldLine) != 0 {
if s.newbuf.Width() >= firstCell && len(oldLine) != 0 {
copy(oldLine[firstCell:], newLine[firstCell:])
}
}
Expand Down Expand Up @@ -1099,16 +1097,16 @@ func (s *Screen) render() {

nonEmpty = s.clearBottom(nonEmpty, partialClear)
for i = 0; i < nonEmpty; i++ {
chg, wasTouched := s.touch[i]
if wasTouched && chg.firstCell != newIndex && chg.lastCell != newIndex {
_, ok := s.touch[i]
if ok {
s.transformLine(i)
changedLines++
}
}
}

// Sync windows and screen
s.touch = make(map[int]lineData)
s.touch = make(map[int]lineData, s.newbuf.Height())

if s.curbuf.Width() != s.newbuf.Width() || s.curbuf.Height() != s.newbuf.Height() {
// Resize the old buffer to match the new buffer.
Expand Down Expand Up @@ -1185,7 +1183,7 @@ func (s *Screen) reset() {
s.cur = Cursor{Position: undefinedPos}
}
s.saved = s.cur
s.touch = make(map[int]lineData)
s.touch = make(map[int]lineData, s.newbuf.Height())
if s.curbuf != nil {
s.curbuf.Clear()
}
Expand All @@ -1209,20 +1207,21 @@ func (s *Screen) Resize(width, height int) bool {

// Clear new columns and lines
if width > oldh {
s.ClearRect(Rect(oldw-2, 0, width-oldw, height))
s.ClearRect(Rect(max(oldw-2, 0), 0, width-oldw, height))
} else if width < oldw {
s.ClearRect(Rect(width-1, 0, oldw-width, height))
s.ClearRect(Rect(max(width-1, 0), 0, oldw-width, height))
}

if height > oldh {
s.ClearRect(Rect(0, oldh-1, width, height-oldh))
s.ClearRect(Rect(0, max(oldh-1, 0), width, height-oldh))
} else if height < oldh {
s.ClearRect(Rect(0, height-1, width, oldh-height))
s.ClearRect(Rect(0, max(height-1, 0), width, oldh-height))
}

s.mu.Lock()
s.newbuf.Resize(width, height)
s.opts.Width, s.opts.Height = width, height
s.oldhash, s.newhash = nil, nil
s.mu.Unlock()

return true
Expand Down

0 comments on commit 0d34f0e

Please sign in to comment.