Skip to content

Commit 0f06d1c

Browse files
committed
Cache runes in chars mode
1 parent 4b6c79a commit 0f06d1c

File tree

3 files changed

+56
-63
lines changed

3 files changed

+56
-63
lines changed

interp/functions.go

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -449,51 +449,3 @@ func (p *interp) sprintf(format string, args []value) (string, error) {
449449
}
450450
return fmt.Sprintf(format, converted...), nil
451451
}
452-
453-
func substrChars(s string, pos int) string {
454-
// Count characters till we get to pos.
455-
chars := 1
456-
start := 0
457-
for start = range s {
458-
chars++
459-
if chars > pos {
460-
break
461-
}
462-
}
463-
if pos >= chars {
464-
start = len(s)
465-
}
466-
return s[start:]
467-
}
468-
469-
func substrLengthChars(s string, pos, length int) string {
470-
// Count characters till we get to pos.
471-
chars := 1
472-
start := 0
473-
for start = range s {
474-
chars++
475-
if chars > pos {
476-
break
477-
}
478-
}
479-
if pos >= chars {
480-
start = len(s)
481-
}
482-
483-
// Count characters from start till we reach length.
484-
chars = 0
485-
end := 0
486-
for end = range s[start:] {
487-
chars++
488-
if chars > length {
489-
break
490-
}
491-
}
492-
if length >= chars {
493-
end = len(s)
494-
} else {
495-
end += start
496-
}
497-
498-
return s[start:end]
499-
}

interp/value.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ const (
2222
type value struct {
2323
typ valueType // Type of value
2424
s string // String value (for typeStr and typeNumStr)
25-
n float64 // Numeric value (for typeNum)
25+
r *[]rune
26+
n float64 // Numeric value (for typeNum)
2627
}
2728

2829
// Create a new null value
@@ -37,12 +38,16 @@ func num(n float64) value {
3738

3839
// Create a new string value
3940
func str(s string) value {
40-
return value{typ: typeStr, s: s}
41+
return value{typ: typeStr, s: s, r: new([]rune)}
42+
}
43+
44+
func strFromRunes(runes []rune) value {
45+
return value{typ: typeStr, s: string(runes), r: &runes}
4146
}
4247

4348
// Create a new value to represent a "numeric string" from an input field
4449
func numStr(s string) value {
45-
return value{typ: typeNumStr, s: s}
50+
return value{typ: typeNumStr, s: s, r: new([]rune)}
4651
}
4752

4853
// Create a numeric value from a Go bool
@@ -53,6 +58,18 @@ func boolean(b bool) value {
5358
return num(0)
5459
}
5560

61+
func (v value) runes(floatFormat string) []rune {
62+
switch v.typ {
63+
case typeStr, typeNumStr:
64+
if *v.r == nil {
65+
*v.r = []rune(v.s)
66+
}
67+
return *v.r
68+
default: // typeNum, typeNull
69+
return []rune(v.str(floatFormat))
70+
}
71+
}
72+
5673
// String returns a string representation of v for debugging.
5774
func (v value) String() string {
5875
switch v.typ {

interp/vm.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,12 +1001,12 @@ func (p *interp) callBuiltin(builtinOp compiler.BuiltinOp) error {
10011001
p.push(num(float64(length)))
10021002

10031003
case compiler.BuiltinLengthArg:
1004-
s := p.toString(p.peekTop())
1004+
s := p.peekTop()
10051005
var length int
10061006
if p.chars {
1007-
length = utf8.RuneCountInString(s)
1007+
length = len(s.runes(p.convertFormat))
10081008
} else {
1009-
length = len(s)
1009+
length = len(p.toString(s))
10101010
}
10111011
p.replaceTop(num(float64(length)))
10121012

@@ -1066,9 +1066,18 @@ func (p *interp) callBuiltin(builtinOp compiler.BuiltinOp) error {
10661066
sValue, posValue := p.peekPop()
10671067
pos := int(posValue.num())
10681068
s := p.toString(sValue)
1069-
var substr string
1069+
var substr value
10701070
if p.chars {
1071-
substr = substrChars(s, pos)
1071+
runes := sValue.runes(p.convertFormat)
1072+
if pos > len(runes) {
1073+
pos = len(runes) + 1
1074+
}
1075+
if pos < 1 {
1076+
pos = 1
1077+
}
1078+
length := len(runes) - pos + 1
1079+
runes = runes[pos-1 : pos-1+length]
1080+
substr = strFromRunes(runes)
10721081
} else {
10731082
if pos > len(s) {
10741083
pos = len(s) + 1
@@ -1077,19 +1086,34 @@ func (p *interp) callBuiltin(builtinOp compiler.BuiltinOp) error {
10771086
pos = 1
10781087
}
10791088
length := len(s) - pos + 1
1080-
substr = s[pos-1 : pos-1+length]
1089+
substr = str(s[pos-1 : pos-1+length])
10811090
}
1082-
p.replaceTop(str(substr))
1091+
p.replaceTop(substr)
10831092

10841093
case compiler.BuiltinSubstrLength:
10851094
posValue, lengthValue := p.popTwo()
10861095
length := int(lengthValue.num())
10871096
pos := int(posValue.num())
1088-
s := p.toString(p.peekTop())
1089-
var substr string
1097+
sValue := p.peekTop()
1098+
var substr value
10901099
if p.chars {
1091-
substr = substrLengthChars(s, pos, length)
1100+
runes := sValue.runes(p.convertFormat)
1101+
if pos > len(runes) {
1102+
pos = len(runes) + 1
1103+
}
1104+
if pos < 1 {
1105+
pos = 1
1106+
}
1107+
maxLength := len(runes) - pos + 1
1108+
if length < 0 {
1109+
length = 0
1110+
}
1111+
if length > maxLength {
1112+
length = maxLength
1113+
}
1114+
substr = strFromRunes(runes[pos-1 : pos-1+length])
10921115
} else {
1116+
s := p.toString(sValue)
10931117
if pos > len(s) {
10941118
pos = len(s) + 1
10951119
}
@@ -1103,9 +1127,9 @@ func (p *interp) callBuiltin(builtinOp compiler.BuiltinOp) error {
11031127
if length > maxLength {
11041128
length = maxLength
11051129
}
1106-
substr = s[pos-1 : pos-1+length]
1130+
substr = str(s[pos-1 : pos-1+length])
11071131
}
1108-
p.replaceTop(str(substr))
1132+
p.replaceTop(substr)
11091133

11101134
case compiler.BuiltinSystem:
11111135
if p.noExec {

0 commit comments

Comments
 (0)