Skip to content

Commit

Permalink
avoids alloc in PhpString.GetBytes()
Browse files Browse the repository at this point in the history
encodes string directly into the underlying byte[] buffer
  • Loading branch information
jakubmisek committed Jan 30, 2022
1 parent ceccb0f commit 260e71b
Showing 1 changed file with 65 additions and 35 deletions.
100 changes: 65 additions & 35 deletions src/Peachpie.Runtime/PhpString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,12 @@ public static BlobChar FromValue(PhpValue value)
// TODO: other types

default:
throw new NotSupportedException(value.TypeCode.ToString());
throw InvalidValueException(value);
}
}

static Exception InvalidValueException(PhpValue value) => new NotSupportedException(value.TypeCode.ToString());

/// <summary>
/// Copies characters to a new array of <see cref="char"/>s.
/// Single-byte chars are encoded to Unicode chars.
Expand Down Expand Up @@ -799,7 +801,7 @@ static void OutputChunk(Context ctx, object chunk)
case Blob b: b.Output(ctx); break;
case char[] carr: ctx.Output.Write(carr); break;
case BlobChar[] barr: WriteChunkAsync(ctx, barr).GetAwaiter().GetResult(); break;
default: throw new ArgumentException(chunk.GetType().ToString());
default: throw InvalidChunkException(chunk);
}
}

Expand Down Expand Up @@ -946,7 +948,7 @@ static void ChunkSubstring(Blob target, int start, ref int count, object chunk,
break;

default:
throw new ArgumentException(chunk.GetType().ToString());
throw InvalidChunkException(chunk);
}
}

Expand Down Expand Up @@ -1017,7 +1019,7 @@ static object ReverseInternal(object chunk)
return ArrayUtils.Reverse(barr);

default:
throw new ArgumentException(chunk.GetType().ToString());
throw InvalidChunkException(chunk);
}
}

Expand Down Expand Up @@ -1063,7 +1065,7 @@ static int GetByteCount(Encoding encoding, object chunk)
case Blob b: return b.GetByteCount(encoding);
case char[] carr: return carr.Length;
case BlobChar[] barr: return barr.Length;
default: throw new ArgumentException(chunk.GetType().ToString());
default: throw InvalidChunkException(chunk);
}
}

Expand Down Expand Up @@ -1123,7 +1125,7 @@ static string ChunkToString(Encoding encoding, object chunk)
Blob b => b.ToString(encoding),
char[] carr => new string(carr),
BlobChar[] barr => BlobChar.ToCharArray(barr, encoding).ToString(),
_ => throw new ArgumentException(chunk.GetType().ToString())
_ => throw InvalidChunkException(chunk),
};
}

Expand All @@ -1145,46 +1147,71 @@ public byte[] ToBytes(Encoding encoding)
else
{
var chunks = _chunks;
return (chunks.GetType() == typeof(object[]))
? ChunkToBytes(encoding, (object[])chunks, _chunksCount)
: ChunkToBytes(encoding, chunks);
if (chunks is object[] array_of_chunks)
{
return ChunkToBytes(encoding, array_of_chunks, _chunksCount);
}
else
{
return ChunkToBytes(encoding, chunks);
}
}
}

static byte[] ChunkToBytes(Encoding encoding, object[] chunks, int count)
{
if (count == 1)
var buffer = new ValueList<byte>();

for (int i = 0; i < count; i++)
{
return ChunkToBytes(encoding, chunks[0]);
ChunkToBytes(ref buffer, encoding, chunks[i]);
}
else
{

var buffer = new ValueList<byte>();
for (int i = 0; i < count; i++)
{
buffer.AddRange(ChunkToBytes(encoding, chunks[i]));
}

return buffer.ToArray();
}
return buffer.ToArray();
}

static byte[] ChunkToBytes(Encoding encoding, object chunk)
{
var buffer = new ValueList<byte>();

ChunkToBytes(ref buffer, encoding, chunk);

return buffer.ToArray();
}

static void ChunkToBytes(ref ValueList<byte> output, Encoding encoding, object chunk)
{
AssertChunkObject(chunk);

switch (chunk)
{
case string str: return encoding.GetBytes(str);
case byte[] barr: return barr;
case Blob b: return b.ToBytes(encoding);
case char[] carr: return encoding.GetBytes(carr);
case BlobChar[] barr: return BlobChar.ToByteArray(barr, encoding);
default: throw new ArgumentException(chunk.GetType().ToString());
case string str:
output.AddBytes(str, encoding);
break;

case byte[] barr:
output.AddRange(barr);
break;

case Blob b:
output.AddRange(b.ToBytes(encoding));
break;

case char[] carr:
output.AddRange(encoding.GetBytes(carr));
break;

case BlobChar[] barr:
output.AddRange(BlobChar.ToByteArray(barr, encoding));
break;

default:
throw InvalidChunkException(chunk);
}
}

static Exception InvalidChunkException(object chunk) => new ArgumentException(chunk != null ? chunk.GetType().ToString() : "null");

#endregion

#region this[int]
Expand Down Expand Up @@ -1301,7 +1328,7 @@ static BlobChar GetCharInChunk(object chunk, int index)
if (chunk.GetType() == typeof(Blob)) return ((Blob)chunk)[index];
if (chunk.GetType() == typeof(BlobChar[])) return ((BlobChar[])chunk)[index];

throw new ArgumentException(chunk.GetType().ToString());
throw InvalidChunkException(chunk);
}

static void SetCharInChunk(ref object chunk, int index, BlobChar ch)
Expand Down Expand Up @@ -1359,7 +1386,7 @@ static void SetCharInChunk(ref object chunk, int index, BlobChar ch)
}
else
{
throw new ArgumentException(chunk.GetType().ToString());
throw InvalidChunkException(chunk);
}
}

Expand Down Expand Up @@ -1389,12 +1416,15 @@ static bool ChunkToBoolean(object chunk)
{
AssertChunkObject(chunk);

if (chunk.GetType() == typeof(string)) return Convert.ToBoolean((string)chunk);
if (chunk.GetType() == typeof(byte[])) return Convert.ToBoolean((byte[])chunk);
if (chunk.GetType() == typeof(Blob)) return ((Blob)chunk).ToBoolean();
if (chunk.GetType() == typeof(char[])) return Convert.ToBoolean((char[])chunk);
if (chunk.GetType() == typeof(BlobChar[])) return Convert.ToBoolean((BlobChar[])chunk);
throw new ArgumentException();
return chunk switch
{
string str => Convert.ToBoolean(str),
byte[] bytes => Convert.ToBoolean(bytes),
Blob blob => blob.ToBoolean(),
char[] chars => Convert.ToBoolean(chars),
BlobChar[] bchars => Convert.ToBoolean(bchars),
_ => throw InvalidChunkException(chunk)
};
}

#endregion
Expand Down

0 comments on commit 260e71b

Please sign in to comment.