micro/rope.go

122 lines
2.5 KiB
Go
Raw Normal View History

2016-03-17 21:27:57 +00:00
package main
import (
"math"
"unicode/utf8"
)
const (
2016-03-19 01:25:45 +00:00
// RopeSplitLength defines how large can a string be before it is split into two nodes
RopeSplitLength = 1000
// RopeJoinLength defines how short can a string be before it is joined
RopeJoinLength = 500
// RopeRebalanceRatio = 1.2
2016-03-17 21:27:57 +00:00
)
2016-03-19 01:25:45 +00:00
// Min takes the min of two ints
func Min(a, b int) int {
2016-03-17 21:27:57 +00:00
if a > b {
return b
}
return a
}
2016-03-19 01:25:45 +00:00
// Max takes the max of two ints
func Max(a, b int) int {
2016-03-17 21:27:57 +00:00
if a > b {
return a
}
return b
}
2016-03-19 01:25:45 +00:00
// A Rope is a data structure for efficiently manipulating large strings
2016-03-17 21:27:57 +00:00
type Rope struct {
left *Rope
right *Rope
value string
valueNil bool
len int
}
2016-03-19 01:25:45 +00:00
// NewRope returns a new rope from a given string
func NewRope(str string) *Rope {
2016-03-17 21:27:57 +00:00
r := new(Rope)
r.value = str
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
2016-03-19 01:25:45 +00:00
r.Adjust()
2016-03-17 21:27:57 +00:00
return r
}
2016-03-19 01:25:45 +00:00
// Adjust modifies the rope so it is more balanced
func (r *Rope) Adjust() {
2016-03-17 21:27:57 +00:00
if !r.valueNil {
2016-03-19 01:25:45 +00:00
if r.len > RopeSplitLength {
2016-03-17 21:27:57 +00:00
divide := int(math.Floor(float64(r.len) / 2))
2016-03-19 01:25:45 +00:00
r.left = NewRope(r.value[:divide])
r.right = NewRope(r.value[divide:])
2016-03-17 21:27:57 +00:00
r.valueNil = true
}
} else {
2016-03-19 01:25:45 +00:00
if r.len < RopeJoinLength {
r.value = r.left.String() + r.right.String()
2016-03-17 21:27:57 +00:00
r.valueNil = false
r.left = nil
r.right = nil
}
}
}
2016-03-19 01:25:45 +00:00
// String returns the string representation of the rope
func (r *Rope) String() string {
2016-03-17 21:27:57 +00:00
if !r.valueNil {
return r.value
}
2016-03-19 01:25:45 +00:00
return r.left.String() + r.right.String()
2016-03-17 21:27:57 +00:00
}
2016-03-19 01:25:45 +00:00
// Remove deletes a slice of the rope from start the to end (exclusive)
func (r *Rope) Remove(start, end int) {
2016-03-17 21:27:57 +00:00
if !r.valueNil {
r.value = string(append([]rune(r.value)[:start], []rune(r.value)[end:]...))
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
} else {
2016-03-19 01:25:45 +00:00
leftStart := Min(start, r.left.len)
leftEnd := Min(end, r.left.len)
rightStart := Max(0, Min(start-r.left.len, r.right.len))
rightEnd := Max(0, Min(end-r.left.len, r.right.len))
2016-03-17 21:27:57 +00:00
if leftStart < r.left.len {
2016-03-19 01:25:45 +00:00
r.left.Remove(leftStart, leftEnd)
2016-03-17 21:27:57 +00:00
}
if rightEnd > 0 {
2016-03-19 01:25:45 +00:00
r.right.Remove(rightStart, rightEnd)
2016-03-17 21:27:57 +00:00
}
r.len = r.left.len + r.right.len
}
2016-03-19 01:25:45 +00:00
r.Adjust()
2016-03-17 21:27:57 +00:00
}
2016-03-19 01:25:45 +00:00
// Insert inserts a string into the rope at a specified position
func (r *Rope) Insert(pos int, value string) {
2016-03-17 21:27:57 +00:00
if !r.valueNil {
first := append([]rune(r.value)[:pos], []rune(value)...)
r.value = string(append(first, []rune(r.value)[pos:]...))
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
} else {
if pos < r.left.len {
2016-03-19 01:25:45 +00:00
r.left.Insert(pos, value)
2016-03-17 21:27:57 +00:00
r.len = r.left.len + r.right.len
} else {
2016-03-19 01:25:45 +00:00
r.right.Insert(pos-r.left.len, value)
2016-03-17 21:27:57 +00:00
}
}
2016-03-19 01:25:45 +00:00
r.Adjust()
2016-03-17 21:27:57 +00:00
}