RawString append.
Closed this issue · 3 comments
Hey @ikorin24 ,
What an amazing job, you're working with unmanaged code. =)
A question, what would be the most suitable way to append/concatenation the RawString's? I haven't found any function that could already do this.
Thank,
Cheers.
Hi, @juliolitwin
The easiest way to combine RawString
is calling RawString.ToString()
and treating them as string
.
RawString foo = ...;
RawString bar = ...;
string combined = $"{foo} and {bar}";
If you also need to take care of performance, you can use RawString.AsSpan()
to copy it to byte[]
and treat it as utf-8.
However, I have not provided any classes or methods to do this. If you want to do this easily,
a library such as ZString might be suitable.
RawString foo = ...;
RawString bar = ...;
using (var sb = Cysharp.Text.ZString.CreateUtf8StringBuilder())
{
sb.AppendLiteral(foo.AsSpan());
sb.Append(" and ");
sb.AppendLiteral(bar.AsSpan());
string combined = sb.ToString();
}
Hey @ikorin24, thanks for answering me.
I understand. The performance issue is really important for me, so the first suggestion is not so viable. I know ZString, but I wanted to be able to keep RawString, it's amazing and has a lot of power. I can make an append, but unfortunately the problem is freeing the memory after use.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal RawString(int length)
{
Debug.Assert(length >= 0);
_ptr = Marshal.AllocHGlobal(length);
AllocationSafety.Add(length);
_length = length;
}
public RawString Append(RawString value)
{
// Get the new length from the two strings.
var totalLength = Length + value.Length;
// Initialize the new buffer.
var appendString = new RawString(totalLength);
fixed (byte* ptr = this.AsSpan())
fixed (byte* ptr2 = value.AsSpan())
{
// Copy the first string for the new buffer.
SpanHelper.CreateReadOnlySpan<byte>(ptr, Length).CopyTo(SpanHelper.CreateSpan<byte>(appendString.GetPtr(), appendString.Length)[..Length]);
// Copy the second string for the new buffer.
SpanHelper.CreateReadOnlySpan<byte>(ptr2, value.Length).CopyTo(SpanHelper.CreateSpan<byte>(appendString.GetPtr(), appendString.Length).Slice(Length, value.Length));
}
return new RawString((byte*)appendString.Ptr, totalLength);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
Marshal.FreeHGlobal(_ptr);
AllocationSafety.Remove(_length);
Unsafe.AsRef(_ptr) = default;
Unsafe.AsRef(_length) = 0;
}
@juliolitwin OK, I'll tell you implementation of RawString
.
RawString
does not have ownership of its memory. It is similar to ReadOnlySpan<byte>
.
All RawString
memories are owned by the XmlObject, which is a pointer to a buffer of bytes read from xml.
This is released when XmlObject.Dispose()
is called.
That means the only way to create a new RawString
is slicing the buffer memory read from xml.
And I think that I will not provide any other way to create RawString
instance.
So, for high performance combining RawString, we should write to Span<byte>
you allocate somewhere.
public static bool TryCombine(RawString str1, RawString str2, Span<byte> destination)
{
if (destination.Length < str1.Length + str2.Length)
{
return false;
}
str1.AsSpan().CopyTo(destination);
str2.AsSpan().CopyTo(destination.Slice(str1.Length));
return true;
}
Most methods in U8XmlParser provides the overload for RawString
and ReadOnlySpan<byte>
, so you can treat them in the same way.