Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize string migration: only return new value if needed #3173

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions migrations/string_normalization/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
package string_normalization

import (
"golang.org/x/text/unicode/norm"

"github.com/onflow/cadence/migrations"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/sema"
Expand All @@ -41,13 +43,30 @@ func (StringNormalizingMigration) Migrate(
_ interpreter.StorageMapKey,
value interpreter.Value,
_ *interpreter.Interpreter,
) (interpreter.Value, error) {
) (
interpreter.Value,
error,
) {

// Normalize strings and characters to NFC.
// If the value is already in NFC, skip the migration.

switch value := value.(type) {
case *interpreter.StringValue:
return interpreter.NewUnmeteredStringValue(value.Str), nil
unnormalizedStr := value.UnnormalizedStr
normalizedStr := norm.NFC.String(unnormalizedStr)
if normalizedStr == unnormalizedStr {
return nil, nil
}
return interpreter.NewStringValue_Unsafe(normalizedStr, unnormalizedStr), nil //nolint:staticcheck

case interpreter.CharacterValue:
return interpreter.NewUnmeteredCharacterValue(value.Str), nil
unnormalizedStr := value.UnnormalizedStr
normalizedStr := norm.NFC.String(unnormalizedStr)
if normalizedStr == unnormalizedStr {
return nil, nil
}
return interpreter.NewCharacterValue_Unsafe(normalizedStr, unnormalizedStr), nil //nolint:staticcheck
}

return nil, nil
Expand Down
30 changes: 27 additions & 3 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,21 @@ type CharacterValue struct {
UnnormalizedStr string
}

func NewUnmeteredCharacterValue(r string) CharacterValue {
func NewUnmeteredCharacterValue(str string) CharacterValue {
return CharacterValue{
Str: norm.NFC.String(r),
UnnormalizedStr: r,
Str: norm.NFC.String(str),
UnnormalizedStr: str,
}
}

// Deprecated: NewStringValue_UnsafeNewCharacterValue_Unsafe creates a new character value
// from the given normalized and unnormalized string.
// NOTE: this function is unsafe, as it does not normalize the string.
// It should only be used for e.g. migration purposes.
func NewCharacterValue_Unsafe(normalizedStr, unnormalizedStr string) CharacterValue {
return CharacterValue{
Str: normalizedStr,
UnnormalizedStr: unnormalizedStr,
}
}

Expand Down Expand Up @@ -1045,6 +1056,19 @@ func NewUnmeteredStringValue(str string) *StringValue {
}
}

// Deprecated: NewStringValue_Unsafe creates a new string value
// from the given normalized and unnormalized string.
// NOTE: this function is unsafe, as it does not normalize the string.
// It should only be used for e.g. migration purposes.
func NewStringValue_Unsafe(normalizedStr, unnormalizedStr string) *StringValue {
return &StringValue{
Str: normalizedStr,
UnnormalizedStr: unnormalizedStr,
// a negative value indicates the length has not been initialized, see Length()
length: -1,
}
}

func NewStringValue(
memoryGauge common.MemoryGauge,
memoryUsage common.MemoryUsage,
Expand Down
Loading