Skip to content

Commit

Permalink
Merge branch 'master' into bastian/3459-emit-capcon-issue-events-2
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Jul 12, 2024
2 parents ee5e3a1 + 80f0dba commit df519bd
Show file tree
Hide file tree
Showing 7 changed files with 1,175 additions and 256 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

170 changes: 135 additions & 35 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,8 +1225,7 @@ func (v *StringValue) Concat(interpreter *Interpreter, other *StringValue, locat
memoryUsage := common.NewStringMemoryUsage(newLength)

// Meter computation as if the two strings were iterated.
length := len(v.Str) + len(other.Str)
interpreter.ReportComputation(common.ComputationKindLoop, uint(length))
interpreter.ReportComputation(common.ComputationKindLoop, uint(newLength))

return NewStringValue(
interpreter,
Expand Down Expand Up @@ -1465,7 +1464,11 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location
panic(errors.NewUnreachableError())
}

return v.Split(invocation.Interpreter, invocation.LocationRange, separator.Str)
return v.Split(
invocation.Interpreter,
invocation.LocationRange,
separator,
)
},
)

Expand All @@ -1475,17 +1478,22 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location
v,
sema.StringTypeReplaceAllFunctionType,
func(invocation Invocation) Value {
of, ok := invocation.Arguments[0].(*StringValue)
original, ok := invocation.Arguments[0].(*StringValue)
if !ok {
panic(errors.NewUnreachableError())
}

with, ok := invocation.Arguments[1].(*StringValue)
replacement, ok := invocation.Arguments[1].(*StringValue)
if !ok {
panic(errors.NewUnreachableError())
}

return v.ReplaceAll(invocation.Interpreter, invocation.LocationRange, of.Str, with.Str)
return v.ReplaceAll(
invocation.Interpreter,
invocation.LocationRange,
original,
replacement,
)
},
)
}
Expand Down Expand Up @@ -1545,29 +1553,84 @@ func (v *StringValue) ToLower(interpreter *Interpreter) *StringValue {
)
}

func (v *StringValue) Split(inter *Interpreter, _ LocationRange, separator string) Value {
func (v *StringValue) Split(inter *Interpreter, locationRange LocationRange, separator *StringValue) *ArrayValue {

// Meter computation as if the string was iterated.
// i.e: linear search to find the split points. This is an estimate.
inter.ReportComputation(common.ComputationKindLoop, uint(len(v.Str)))
if len(separator.Str) == 0 {
return v.Explode(inter, locationRange)
}

split := strings.Split(v.Str, separator)
count := v.count(inter, locationRange, separator) + 1

var index int
count := len(split)
partIndex := 0

remaining := v

return NewArrayValueWithIterator(
inter,
VarSizedArrayOfStringType,
common.ZeroAddress,
uint64(count),
func() Value {
if index >= count {

inter.ReportComputation(common.ComputationKindLoop, 1)

if partIndex >= count {
return nil
}

str := split[index]
index++
// Set the remainder as the last part
if partIndex == count-1 {
partIndex++
return remaining
}

separatorCharacterIndex, _ := remaining.indexOf(inter, separator)
if separatorCharacterIndex < 0 {
return nil
}

partIndex++

part := remaining.slice(
0,
separatorCharacterIndex,
locationRange,
)

remaining = remaining.slice(
separatorCharacterIndex+separator.Length(),
remaining.Length(),
locationRange,
)

return part
},
)
}

// Explode returns a Cadence array of type [String], where each element is a single character of the string
func (v *StringValue) Explode(inter *Interpreter, locationRange LocationRange) *ArrayValue {

iterator := v.Iterator(inter, locationRange)

return NewArrayValueWithIterator(
inter,
VarSizedArrayOfStringType,
common.ZeroAddress,
uint64(v.Length()),
func() Value {
value := iterator.Next(inter, locationRange)
if value == nil {
return nil
}

character, ok := value.(CharacterValue)
if !ok {
panic(errors.NewUnreachableError())
}

str := character.Str

return NewStringValue(
inter,
common.NewStringMemoryUsage(len(str)),
Expand All @@ -1579,23 +1642,62 @@ func (v *StringValue) Split(inter *Interpreter, _ LocationRange, separator strin
)
}

func (v *StringValue) ReplaceAll(inter *Interpreter, _ LocationRange, of string, with string) *StringValue {
// Over-estimate the resulting string length.
// In the worst case, `of` can be empty in which case, `with` will be added at every index.
// e.g. `of` = "", `v` = "ABC", `with` = "1": result = "1A1B1C1".
strLen := len(v.Str)
lengthOverEstimate := (2*strLen + 1) * len(with)
func (v *StringValue) ReplaceAll(
inter *Interpreter,
locationRange LocationRange,
original *StringValue,
replacement *StringValue,
) *StringValue {

memoryUsage := common.NewStringMemoryUsage(lengthOverEstimate)
count := v.count(inter, locationRange, original)
if count == 0 {
return v
}

newByteLength := len(v.Str) + count*(len(replacement.Str)-len(original.Str))

memoryUsage := common.NewStringMemoryUsage(newByteLength)

// Meter computation as if the string was iterated.
inter.ReportComputation(common.ComputationKindLoop, uint(strLen))
inter.ReportComputation(common.ComputationKindLoop, uint(len(v.Str)))

remaining := v

return NewStringValue(
inter,
memoryUsage,
func() string {
return strings.ReplaceAll(v.Str, of, with)
var b strings.Builder
b.Grow(newByteLength)
for i := 0; i < count; i++ {

var originalCharacterIndex, originalByteOffset int
if original.Length() == 0 {
if i > 0 {
originalCharacterIndex = 1

remaining.prepareGraphemes()
remaining.graphemes.Next()
_, originalByteOffset = remaining.graphemes.Positions()
}
} else {
originalCharacterIndex, originalByteOffset = remaining.indexOf(inter, original)
if originalCharacterIndex < 0 {
panic(errors.NewUnreachableError())
}
}

b.WriteString(remaining.Str[:originalByteOffset])
b.WriteString(replacement.Str)

remaining = remaining.slice(
originalCharacterIndex+original.Length(),
remaining.Length(),
locationRange,
)
}
b.WriteString(remaining.Str)
return b.String()
},
)
}
Expand Down Expand Up @@ -1806,14 +1908,14 @@ func (v *StringValue) isGraphemeBoundaryEndPrepared(end int) bool {
}

func (v *StringValue) IndexOf(inter *Interpreter, other *StringValue) IntValue {
index := v.indexOf(inter, other)
index, _ := v.indexOf(inter, other)
return NewIntValueFromInt64(inter, int64(index))
}

func (v *StringValue) indexOf(inter *Interpreter, other *StringValue) int {
func (v *StringValue) indexOf(inter *Interpreter, other *StringValue) (characterIndex int, byteOffset int) {

if len(other.Str) == 0 {
return 0
return 0, 0
}

// Meter computation as if the string was iterated.
Expand All @@ -1826,9 +1928,6 @@ func (v *StringValue) indexOf(inter *Interpreter, other *StringValue) int {
// - 'CharacterIndex' indicates Cadence characters (grapheme clusters)
// - 'ByteOffset' indicates bytes

// The resulting index, in terms of Cadence characters (grapheme clusters)
var characterIndex int

// Find the position of the substring in the string,
// by using strings.Index with an increasing start byte offset.
//
Expand Down Expand Up @@ -1864,19 +1963,20 @@ func (v *StringValue) indexOf(inter *Interpreter, other *StringValue) int {
if v.seekGraphemeBoundaryStartPrepared(absoluteFoundByteOffset, &characterIndex) &&
v.isGraphemeBoundaryEndPrepared(absoluteFoundByteOffset+len(other.Str)) {

return characterIndex
return characterIndex, absoluteFoundByteOffset
}

// Restore the grapheme iterator and character index
v.graphemes = &graphemesBackup
characterIndex = characterIndexBackup
}

return -1
return -1, -1
}

func (v *StringValue) Contains(inter *Interpreter, other *StringValue) BoolValue {
return AsBoolValue(v.indexOf(inter, other) >= 0)
characterIndex, _ := v.indexOf(inter, other)
return AsBoolValue(characterIndex >= 0)
}

func (v *StringValue) Count(inter *Interpreter, locationRange LocationRange, other *StringValue) IntValue {
Expand All @@ -1896,7 +1996,7 @@ func (v *StringValue) count(inter *Interpreter, locationRange LocationRange, oth
count := 0

for {
index := remaining.indexOf(inter, other)
index, _ := remaining.indexOf(inter, other)
if index == -1 {
return count
}
Expand Down
Loading

0 comments on commit df519bd

Please sign in to comment.