mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
go/constant: add StringLen function
For fast computation of a string value's length w/o the need to first "materialize" the actual string. Use StringLen in the type checker where appropriate. Fixes #79042. Fixes #78346. Change-Id: Id602b060176b771d73fc737e0a37a9707f235a02 Reviewed-on: https://go-review.googlesource.com/c/go/+/772320 Auto-Submit: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
parent
15fd4ff942
commit
2677fe9bbe
10 changed files with 73 additions and 30 deletions
1
api/next/79042.txt
Normal file
1
api/next/79042.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
pkg go/constant, func StringLen(Value) int64 #79042
|
||||
1
doc/next/6-stdlib/99-minor/go/constant/79042.md
Normal file
1
doc/next/6-stdlib/99-minor/go/constant/79042.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
The new [StringLen] function returns the length of a string [Value]. For an [Unknown] value, the length is 0.
|
||||
|
|
@ -153,7 +153,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
if isString(t) && id == _Len {
|
||||
if x.mode() == constant_ {
|
||||
mode = constant_
|
||||
val = constant.MakeInt64(int64(len(constant.StringVal(x.val))))
|
||||
val = constant.MakeInt64(constant.StringLen(x.val))
|
||||
} else {
|
||||
mode = value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,16 +49,15 @@ func (check *Checker) overflow(x *operand, opPos syntax.Pos) {
|
|||
return
|
||||
}
|
||||
|
||||
const maxLen = int(2e9) // cmd/internal/obj.MaxSymSize
|
||||
// Disable the length check for now, as calling constant.StringVal
|
||||
// eagerly constructs the string and can lead to significant memory
|
||||
// usage increase. We may want a StringLen function.
|
||||
// TODO(go.dev/issue/78346): reenable the check.
|
||||
if false && x.val.Kind() == constant.String && len(constant.StringVal(x.val)) > maxLen {
|
||||
check.errorf(atPos(opPos), InvalidConstVal, "constant string too long (%d bytes > %d bytes)",
|
||||
len(constant.StringVal(x.val)), maxLen)
|
||||
x.val = constant.MakeUnknown()
|
||||
return
|
||||
// String values must not become arbitrarily long (go.dev/issue/78346).
|
||||
const maxLen = int64(2e9) // cmd/internal/obj.MaxSymSize
|
||||
if x.val.Kind() == constant.String {
|
||||
len := constant.StringLen(x.val)
|
||||
if len > maxLen {
|
||||
check.errorf(atPos(opPos), InvalidConstVal, "constant string too long (%d bytes > %d bytes)", len, maxLen)
|
||||
x.val = constant.MakeUnknown()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
|
|||
if isString(typ) {
|
||||
valid = true
|
||||
if x.mode() == constant_ {
|
||||
length = int64(len(constant.StringVal(x.val)))
|
||||
length = constant.StringLen(x.val)
|
||||
}
|
||||
// an indexed string always yields a byte value
|
||||
// (not a constant) even if the string and the
|
||||
|
|
@ -302,7 +302,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
|
|||
}
|
||||
valid = true
|
||||
if x.mode() == constant_ {
|
||||
length = int64(len(constant.StringVal(x.val)))
|
||||
length = constant.StringLen(x.val)
|
||||
}
|
||||
// spec: "For untyped string operands the result
|
||||
// is a non-constant value of type string."
|
||||
|
|
|
|||
|
|
@ -137,15 +137,13 @@ func (x *stringVal) String() string {
|
|||
// concatenation. See golang.org/issue/23348.
|
||||
func (x *stringVal) string() string {
|
||||
x.mu.Lock()
|
||||
defer x.mu.Unlock()
|
||||
if x.l != nil {
|
||||
x.s = strings.Join(reverse(x.appendReverse(nil)), "")
|
||||
x.l = nil
|
||||
x.r = nil
|
||||
}
|
||||
s := x.s
|
||||
x.mu.Unlock()
|
||||
|
||||
return s
|
||||
return x.s
|
||||
}
|
||||
|
||||
// reverse reverses x in place and returns it.
|
||||
|
|
@ -609,6 +607,30 @@ func Val(x Value) any {
|
|||
}
|
||||
}
|
||||
|
||||
// StringLen returns the length of x if x is a [String].
|
||||
// If x is [Unknown], the result is 0.
|
||||
// In all other cases, the function panics.
|
||||
func StringLen(x Value) int64 {
|
||||
switch x := x.(type) {
|
||||
case *stringVal:
|
||||
return x.len()
|
||||
case unknownVal:
|
||||
return 0
|
||||
default:
|
||||
panic(fmt.Sprintf("%v not a String", x))
|
||||
}
|
||||
}
|
||||
|
||||
// len computes and returns the length of x without constructing the entire string.
|
||||
func (x *stringVal) len() int64 {
|
||||
x.mu.Lock()
|
||||
defer x.mu.Unlock()
|
||||
if x.l != nil {
|
||||
return x.l.len() + x.r.len()
|
||||
}
|
||||
return int64(len(x.s))
|
||||
}
|
||||
|
||||
// Make returns the [Value] for x.
|
||||
//
|
||||
// type of x result Kind
|
||||
|
|
|
|||
|
|
@ -437,6 +437,27 @@ func TestString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStringLen(t *testing.T) {
|
||||
tests := []struct {
|
||||
x Value
|
||||
want int64
|
||||
}{
|
||||
{MakeUnknown(), 0},
|
||||
{val(`""`), 0},
|
||||
{val(`"foo"`), 3},
|
||||
{val(`"世界"`), 6},
|
||||
{BinaryOp(val(`"foo"`), token.ADD, val(`"bar"`)), 6},
|
||||
{BinaryOp(val(`"世界"`), token.ADD, val(`"!"`)), 7},
|
||||
{BinaryOp(val(`"a"`), token.ADD, BinaryOp(val(`"b"`), token.ADD, val(`"c"`))), 3},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got := StringLen(test.x); got != test.want {
|
||||
t.Errorf("StringLen(%v): got %d; want %d", test.x, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support functions
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
if isString(t) && id == _Len {
|
||||
if x.mode() == constant_ {
|
||||
mode = constant_
|
||||
val = constant.MakeInt64(int64(len(constant.StringVal(x.val))))
|
||||
val = constant.MakeInt64(constant.StringLen(x.val))
|
||||
} else {
|
||||
mode = value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,16 +51,15 @@ func (check *Checker) overflow(x *operand, opPos token.Pos) {
|
|||
return
|
||||
}
|
||||
|
||||
const maxLen = int(2e9) // cmd/internal/obj.MaxSymSize
|
||||
// Disable the length check for now, as calling constant.StringVal
|
||||
// eagerly constructs the string and can lead to significant memory
|
||||
// usage increase. We may want a StringLen function.
|
||||
// TODO(go.dev/issue/78346): reenable the check.
|
||||
if false && x.val.Kind() == constant.String && len(constant.StringVal(x.val)) > maxLen {
|
||||
check.errorf(atPos(opPos), InvalidConstVal, "constant string too long (%d bytes > %d bytes)",
|
||||
len(constant.StringVal(x.val)), maxLen)
|
||||
x.val = constant.MakeUnknown()
|
||||
return
|
||||
// String values must not become arbitrarily long (go.dev/issue/78346).
|
||||
const maxLen = int64(2e9) // cmd/internal/obj.MaxSymSize
|
||||
if x.val.Kind() == constant.String {
|
||||
len := constant.StringLen(x.val)
|
||||
if len > maxLen {
|
||||
check.errorf(atPos(opPos), InvalidConstVal, "constant string too long (%d bytes > %d bytes)", len, maxLen)
|
||||
x.val = constant.MakeUnknown()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func (check *Checker) indexExpr(x *operand, e *indexedExpr) (isFuncInst bool) {
|
|||
if isString(typ) {
|
||||
valid = true
|
||||
if x.mode() == constant_ {
|
||||
length = int64(len(constant.StringVal(x.val)))
|
||||
length = constant.StringLen(x.val)
|
||||
}
|
||||
// an indexed string always yields a byte value
|
||||
// (not a constant) even if the string and the
|
||||
|
|
@ -307,7 +307,7 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
|
|||
}
|
||||
valid = true
|
||||
if x.mode() == constant_ {
|
||||
length = int64(len(constant.StringVal(x.val)))
|
||||
length = constant.StringLen(x.val)
|
||||
}
|
||||
// spec: "For untyped string operands the result
|
||||
// is a non-constant value of type string."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue