Introduce TargetString to eliminate text GC.

This commit is contained in:
2022-11-21 14:09:01 +08:00
parent 784c3fc338
commit 109b489104
4 changed files with 154 additions and 10 deletions

View File

@@ -0,0 +1,120 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Cryville.Common.Buffers {
/// <summary>
/// An auto-resized <see cref="char" /> array as a variable-length string used as a target that is modified frequently.
/// </summary>
public class TargetString : IEnumerable<char> {
public event Action OnUpdate;
char[] _arr;
bool _invalidated;
/// <summary>
/// Creates an instance of the <see cref="TargetString" /> class with a capacity of 16.
/// </summary>
public TargetString() : this(16) { }
/// <summary>
/// Creates an instance of the <see cref="TargetString" /> class.
/// </summary>
/// <param name="capacity">The initial capacity of the string.</param>
public TargetString(int capacity) {
_arr = new char[capacity];
}
/// <summary>
/// Gets or sets one of the characters in the string.
/// </summary>
/// <param name="index">The zero-based index of the character.</param>
/// <returns>The character at the given index.</returns>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index" /> is less than 0 or not less than <see cref="Length" />.</exception>
/// <remarks>
/// <para>Set <see cref="Length" /> to a desired value before updating the characters.</para>
/// <para>Call <see cref=" Validate" /> after all the characters are updated.</para>
/// </remarks>
public char this[int index] {
get {
if (index < 0 || index >= m_length)
throw new ArgumentOutOfRangeException("index");
return _arr[index];
}
set {
if (index < 0 || index >= m_length)
throw new ArgumentOutOfRangeException("index");
if (_arr[index] == value) return;
_arr[index] = value;
_invalidated = true;
}
}
int m_length;
/// <summary>
/// The length of the string.
/// </summary>
public int Length {
get {
return m_length;
}
set {
if (m_length == value) return;
if (_arr.Length < value) {
var len = m_length;
while (len < value) len *= 2;
var arr2 = new char[len];
Array.Copy(_arr, arr2, m_length);
_arr = arr2;
}
m_length = value;
_invalidated = true;
}
}
/// <summary>
/// Validates the string.
/// </summary>
public void Validate() {
if (!_invalidated) return;
_invalidated = false;
var ev = OnUpdate;
if (ev != null) OnUpdate.Invoke();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public IEnumerator<char> GetEnumerator() {
return new Enumerator(this);
}
class Enumerator : IEnumerator<char> {
TargetString _self;
int _index = -1;
public Enumerator(TargetString self) { _self = self; }
public char Current {
get {
if (_index < 0)
throw new InvalidOperationException(_index == -1 ? "Enum not started" : "Enum ended");
return _self[_index];
}
}
object IEnumerator.Current { get { return Current; } }
public void Dispose() {
_index = -2;
}
public bool MoveNext() {
if (_index == -2) return false;
_index++;
if (_index >= _self.Length) {
_index = -2;
return false;
}
return true;
}
public void Reset() {
_index = -1;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0fc34ac257793d4883a9cfcdb6941b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,5 @@
using Cryville.Common.Pdt;
using Cryville.Common.Buffers;
using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using UnityEngine;
@@ -7,7 +8,7 @@ namespace Cryville.Crtr.Components {
public class SpriteText : SpriteBase {
public SpriteText() {
SubmitProperty("frames", new op_set_frames(this));
SubmitProperty("value", new PropOp.String(v => Value = v));
SubmitProperty("value", new PropOp.TargetString(() => Value));
SubmitProperty("size", new PropOp.Float(v => Size = v));
SubmitProperty("spacing", new PropOp.Float(v => Spacing = v));
SubmitProperty("opacity", new PropOp.Float(v => Opacity = v));
@@ -48,14 +49,8 @@ namespace Cryville.Crtr.Components {
set { m_frames = value; UpdateFrames(); UpdateScale(); }
}
public string m_value;
public string Value {
get { return m_value; }
set {
if (m_value == value) return;
m_value = value; UpdateScale();
}
}
readonly TargetString m_value = new TargetString();
public TargetString Value { get { return m_value; } }
public float m_size;
public float Size {
@@ -175,6 +170,7 @@ namespace Cryville.Crtr.Components {
UpdateFrames();
mesh.Mesh.Clear();
UpdateScale();
Value.OnUpdate += UpdateScale;
}
}
}

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using RBeatTime = Cryville.Crtr.BeatTime;
using RTargetString = Cryville.Common.Buffers.TargetString;
namespace Cryville.Crtr {
public abstract class PropOp : PdtOperator {
@@ -45,6 +46,22 @@ namespace Cryville.Crtr {
_cb(GetOperand(0).AsString());
}
}
public class TargetString : PropOp {
readonly Func<RTargetString> _cb;
public TargetString(Func<RTargetString> cb) { _cb = cb; }
protected override unsafe void Execute() {
var target = _cb();
var op = GetOperand(0);
if (op.Type != PdtInternalType.String) throw new ArgumentException("Not a string");
var ptr = (byte*)op.TrustedAsOfLength(op.Length);
var len = *(int*)ptr;
target.Length = len;
var cptr = (char*)(ptr + sizeof(int));
for (int i = 0; i < len; i++)
target[i] = cptr[i];
target.Validate();
}
}
public class Identifier : PropOp {
readonly Action<int> _cb;
public Identifier(Action<int> cb) { _cb = cb; }