Skip to content

Commit

Permalink
Cache string size
Browse files Browse the repository at this point in the history
  • Loading branch information
federicotdn committed Oct 4, 2023
1 parent c11de03 commit d5cf55d
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 15 deletions.
2 changes: 1 addition & 1 deletion core/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func (ec *execContext) concat(args ...lispObject) (lispObject, error) {
case stringp(arg):
s := xString(arg)
result += s.str()
multibyte = multibyte || s.multibyte
multibyte = multibyte || s.multibytep()
case arg == ec.nil_:
case consp(arg):
fallthrough
Expand Down
2 changes: 1 addition & 1 deletion core/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func xString(obj lispObject) *lispString {
}

func xStringMultibytep(obj lispObject) bool {
return xString(obj).multibyte
return xString(obj).multibytep()
}

func xStringValue(obj lispObject) string {
Expand Down
49 changes: 38 additions & 11 deletions core/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,50 @@ import (
"unicode/utf8"
)

// |------------+---------------------------------+-------------------------------------|
// | | immutable (val only) | mutable (valMut only, != nil) |
// |------------+---------------------------------+-------------------------------------|
// | size_ < 0 | Unibyte immutable string | Unibyte mutable string |
// | | Ideal for storing ASCII runes | Good for use as a raw bytes buffer. |
// | | that will not be modified. | Will copy bytes when calling str()! |
// |------------+---------------------------------+-------------------------------------|
// | size_ >= 0 | Multibyte immutable string | Multibyte mutable string |
// | | Ideal for storing Unicode runes | Will copy bytes when calling str()! |
// | | that will not be modified. | |
// |------------+---------------------------------+-------------------------------------|
type lispString struct {
valMut []byte
val string
multibyte bool
valMut []byte
val string
size_ int
}

func newStringInternal(val string, multibyte bool, size_ int) *lispString {
if multibyte && size_ < 0 {
size_ = utf8.RuneCountInString(val)
}
return &lispString{
val: val,
size_: size_,
}
}

func newString(val string, multibyte bool) *lispString {
return &lispString{val: val, multibyte: multibyte}
return newStringInternal(val, multibyte, -1)
}

func newUniOrMultibyteString(val string) *lispString {
for _, r := range val {
if !asciiCharp(r) {
return newString(val, true)
}
multibyte := false
size_ := utf8.RuneCountInString(val)
if size_ < len(val) {
multibyte = true
} else {
size_ = -1
}
return newString(val, false)
return newStringInternal(val, multibyte, size_)
}

func (ls *lispString) multibytep() bool {
return ls.size_ >= 0
}

func (ls *lispString) str() string {
Expand All @@ -32,9 +59,9 @@ func (ls *lispString) str() string {
}

func (ls *lispString) size() int {
if ls.multibyte {
if ls.multibytep() {
if ls.valMut == nil {
return utf8.RuneCountInString(ls.val)
return ls.size_
}
return utf8.RuneCount(ls.valMut)
}
Expand Down
18 changes: 16 additions & 2 deletions core/string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ package core

import "testing"

func TestNewString(t *testing.T) {
t.Parallel()

s := newString("foo", false)
if s.multibytep() {
t.Fail()
}

s = newString("árbol", true)
if !s.multibytep() {
t.Fail()
}
}

func TestUnibyteString(t *testing.T) {
t.Parallel()

Expand All @@ -15,7 +29,7 @@ func TestUnibyteString(t *testing.T) {
if s.str() != "hello" {
t.Fail()
}
if s.multibyte {
if s.multibytep() {
t.Fail()
}
}
Expand All @@ -33,7 +47,7 @@ func TestMultibyteString(t *testing.T) {
if s.str() != "ñandú" {
t.Fail()
}
if !s.multibyte {
if !s.multibytep() {
t.Fail()
}
}

0 comments on commit d5cf55d

Please sign in to comment.