Reduce memory footprint of Item struct

This commit is contained in:
Junegunn Choi
2017-07-16 23:31:19 +09:00
parent 4b59ced08f
commit 9e85cba0d0
12 changed files with 139 additions and 122 deletions

View File

@@ -3,63 +3,81 @@ package util
import (
"unicode"
"unicode/utf8"
"unsafe"
)
type Chars struct {
runes []rune
bytes []byte
slice []byte // or []rune
inBytes bool
trimLengthKnown bool
trimLength uint16
// XXX Piggybacking item index here is a horrible idea. But I'm trying to
// minimize the memory footprint by not wasting padded spaces.
Index int32
}
// ToChars converts byte array into rune array
func ToChars(bytea []byte) Chars {
func ToChars(bytes []byte) Chars {
var runes []rune
ascii := true
numBytes := len(bytea)
inBytes := true
numBytes := len(bytes)
for i := 0; i < numBytes; {
if bytea[i] < utf8.RuneSelf {
if !ascii {
runes = append(runes, rune(bytea[i]))
if bytes[i] < utf8.RuneSelf {
if !inBytes {
runes = append(runes, rune(bytes[i]))
}
i++
} else {
if ascii {
ascii = false
if inBytes {
inBytes = false
runes = make([]rune, i, numBytes)
for j := 0; j < i; j++ {
runes[j] = rune(bytea[j])
runes[j] = rune(bytes[j])
}
}
r, sz := utf8.DecodeRune(bytea[i:])
r, sz := utf8.DecodeRune(bytes[i:])
i += sz
runes = append(runes, r)
}
}
if ascii {
return Chars{bytes: bytea}
if inBytes {
return Chars{slice: bytes, inBytes: inBytes}
}
return Chars{runes: runes}
return RunesToChars(runes)
}
func RunesToChars(runes []rune) Chars {
return Chars{runes: runes}
return Chars{slice: *(*[]byte)(unsafe.Pointer(&runes)), inBytes: false}
}
func (chars *Chars) optionalRunes() []rune {
if chars.inBytes {
return nil
}
return *(*[]rune)(unsafe.Pointer(&chars.slice))
}
func (chars *Chars) Get(i int) rune {
if chars.runes != nil {
return chars.runes[i]
if runes := chars.optionalRunes(); runes != nil {
return runes[i]
}
return rune(chars.bytes[i])
return rune(chars.slice[i])
}
func (chars *Chars) Length() int {
if chars.runes != nil {
return len(chars.runes)
if runes := chars.optionalRunes(); runes != nil {
return len(runes)
}
return len(chars.bytes)
return len(chars.slice)
}
// TrimLength returns the length after trimming leading and trailing whitespaces
func (chars *Chars) TrimLength() int {
func (chars *Chars) TrimLength() uint16 {
if chars.trimLengthKnown {
return chars.trimLength
}
chars.trimLengthKnown = true
var i int
len := chars.Length()
for i = len - 1; i >= 0; i-- {
@@ -80,7 +98,8 @@ func (chars *Chars) TrimLength() int {
break
}
}
return i - j + 1
chars.trimLength = AsUint16(i - j + 1)
return chars.trimLength
}
func (chars *Chars) TrailingWhitespaces() int {
@@ -96,28 +115,40 @@ func (chars *Chars) TrailingWhitespaces() int {
}
func (chars *Chars) ToString() string {
if chars.runes != nil {
return string(chars.runes)
if runes := chars.optionalRunes(); runes != nil {
return string(runes)
}
return string(chars.bytes)
return string(chars.slice)
}
func (chars *Chars) ToRunes() []rune {
if chars.runes != nil {
return chars.runes
if runes := chars.optionalRunes(); runes != nil {
return runes
}
runes := make([]rune, len(chars.bytes))
for idx, b := range chars.bytes {
bytes := chars.slice
runes := make([]rune, len(bytes))
for idx, b := range bytes {
runes[idx] = rune(b)
}
return runes
}
func (chars *Chars) Slice(b int, e int) Chars {
if chars.runes != nil {
return Chars{runes: chars.runes[b:e]}
func (chars *Chars) CopyRunes(dest []rune) {
if runes := chars.optionalRunes(); runes != nil {
copy(dest, runes)
return
}
return Chars{bytes: chars.bytes[b:e]}
for idx, b := range chars.slice {
dest[idx] = rune(b)
}
return
}
func (chars *Chars) Slice(b int, e int) Chars {
if runes := chars.optionalRunes(); runes != nil {
return RunesToChars(runes[b:e])
}
return Chars{slice: chars.slice[b:e], inBytes: true}
}
func (chars *Chars) Split(delimiter string) []Chars {