Use spans to improve performance in StreamExts.
Also avoid ReadBytes calls that allocate a buffer by either updating the stream position (if not interested in the bytes), by reusing an input buffer (if interested in the bytes), or using a stackalloc buffer to avoid the allocation (for small reads).
This commit is contained in:
@@ -165,9 +165,9 @@ namespace OpenRA
|
|||||||
if (length < 0x80)
|
if (length < 0x80)
|
||||||
return length;
|
return length;
|
||||||
|
|
||||||
var data = new byte[4];
|
Span<byte> data = stackalloc byte[4];
|
||||||
s.ReadBytes(data, 0, Math.Min(length & 0x7F, 4));
|
s.ReadBytes(data[..Math.Min(length & 0x7F, 4)]);
|
||||||
return BitConverter.ToInt32(data.ToArray(), 0);
|
return BitConverter.ToInt32(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int TripletFullLength(int dataLength)
|
static int TripletFullLength(int dataLength)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace OpenRA.FileFormats
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var length = IPAddress.NetworkToHostOrder(s.ReadInt32());
|
var length = IPAddress.NetworkToHostOrder(s.ReadInt32());
|
||||||
var type = Encoding.UTF8.GetString(s.ReadBytes(4));
|
var type = s.ReadASCII(4);
|
||||||
var content = s.ReadBytes(length);
|
var content = s.ReadBytes(length);
|
||||||
/*var crc = */s.ReadInt32();
|
/*var crc = */s.ReadInt32();
|
||||||
|
|
||||||
@@ -287,10 +287,10 @@ namespace OpenRA.FileFormats
|
|||||||
|
|
||||||
var typeBytes = Encoding.ASCII.GetBytes(type);
|
var typeBytes = Encoding.ASCII.GetBytes(type);
|
||||||
output.Write(IPAddress.HostToNetworkOrder((int)input.Length));
|
output.Write(IPAddress.HostToNetworkOrder((int)input.Length));
|
||||||
output.WriteArray(typeBytes);
|
output.Write(typeBytes);
|
||||||
|
|
||||||
var data = input.ReadAllBytes();
|
var data = input.ReadAllBytes();
|
||||||
output.WriteArray(data);
|
output.Write(data);
|
||||||
|
|
||||||
var crc32 = new Crc32();
|
var crc32 = new Crc32();
|
||||||
crc32.Update(typeBytes);
|
crc32.Update(typeBytes);
|
||||||
@@ -302,7 +302,7 @@ namespace OpenRA.FileFormats
|
|||||||
{
|
{
|
||||||
using (var output = new MemoryStream())
|
using (var output = new MemoryStream())
|
||||||
{
|
{
|
||||||
output.WriteArray(Signature);
|
output.Write(Signature);
|
||||||
using (var header = new MemoryStream())
|
using (var header = new MemoryStream())
|
||||||
{
|
{
|
||||||
header.Write(IPAddress.HostToNetworkOrder(Width));
|
header.Write(IPAddress.HostToNetworkOrder(Width));
|
||||||
@@ -371,7 +371,7 @@ namespace OpenRA.FileFormats
|
|||||||
{
|
{
|
||||||
using (var text = new MemoryStream())
|
using (var text = new MemoryStream())
|
||||||
{
|
{
|
||||||
text.WriteArray(Encoding.ASCII.GetBytes(kv.Key + (char)0 + kv.Value));
|
text.Write(Encoding.ASCII.GetBytes(kv.Key + (char)0 + kv.Value));
|
||||||
WritePngChunk(output, "tEXt", text);
|
WritePngChunk(output, "tEXt", text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace OpenRA.FileFormats
|
|||||||
throw new NotSupportedException($"Metadata version {version} is not supported");
|
throw new NotSupportedException($"Metadata version {version} is not supported");
|
||||||
|
|
||||||
// Read game info (max 100K limit as a safeguard against corrupted files)
|
// Read game info (max 100K limit as a safeguard against corrupted files)
|
||||||
var data = fs.ReadString(Encoding.UTF8, 1024 * 100);
|
var data = fs.ReadLengthPrefixedString(Encoding.UTF8, 1024 * 100);
|
||||||
GameInfo = GameInformation.Deserialize(data);
|
GameInfo = GameInformation.Deserialize(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ namespace OpenRA.FileFormats
|
|||||||
{
|
{
|
||||||
// Write lobby info data
|
// Write lobby info data
|
||||||
writer.Flush();
|
writer.Flush();
|
||||||
dataLength += writer.BaseStream.WriteString(Encoding.UTF8, GameInfo.Serialize());
|
dataLength += writer.BaseStream.WriteLengthPrefixedString(Encoding.UTF8, GameInfo.Serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write total length & end marker
|
// Write total length & end marker
|
||||||
|
|||||||
@@ -117,10 +117,7 @@ namespace OpenRA.FileSystem
|
|||||||
|
|
||||||
void Commit()
|
void Commit()
|
||||||
{
|
{
|
||||||
var pos = pkgStream.Position;
|
File.WriteAllBytes(Name, pkgStream.ToArray());
|
||||||
pkgStream.Position = 0;
|
|
||||||
File.WriteAllBytes(Name, pkgStream.ReadBytes((int)pkgStream.Length));
|
|
||||||
pkgStream.Position = pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(string filename, byte[] contents)
|
public void Update(string filename, byte[] contents)
|
||||||
|
|||||||
@@ -261,14 +261,14 @@ namespace OpenRA.Network
|
|||||||
{
|
{
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
ms.Write(packet.Length);
|
ms.Write(packet.Length);
|
||||||
ms.WriteArray(packet);
|
ms.Write(packet);
|
||||||
|
|
||||||
foreach (var s in queuedSyncPackets)
|
foreach (var s in queuedSyncPackets)
|
||||||
{
|
{
|
||||||
var q = OrderIO.SerializeSync(s);
|
var q = OrderIO.SerializeSync(s);
|
||||||
|
|
||||||
ms.Write(q.Length);
|
ms.Write(q.Length);
|
||||||
ms.WriteArray(q);
|
ms.Write(q);
|
||||||
|
|
||||||
sentSync.Enqueue(s);
|
sentSync.Enqueue(s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,10 +122,10 @@ namespace OpenRA.Network
|
|||||||
LastSyncFrame = rs.ReadInt32();
|
LastSyncFrame = rs.ReadInt32();
|
||||||
lastSyncPacket = rs.ReadBytes(Order.SyncHashOrderLength);
|
lastSyncPacket = rs.ReadBytes(Order.SyncHashOrderLength);
|
||||||
|
|
||||||
var globalSettings = MiniYaml.FromString(rs.ReadString(Encoding.UTF8, Connection.MaxOrderLength));
|
var globalSettings = MiniYaml.FromString(rs.ReadLengthPrefixedString(Encoding.UTF8, Connection.MaxOrderLength));
|
||||||
GlobalSettings = Session.Global.Deserialize(globalSettings[0].Value);
|
GlobalSettings = Session.Global.Deserialize(globalSettings[0].Value);
|
||||||
|
|
||||||
var slots = MiniYaml.FromString(rs.ReadString(Encoding.UTF8, Connection.MaxOrderLength));
|
var slots = MiniYaml.FromString(rs.ReadLengthPrefixedString(Encoding.UTF8, Connection.MaxOrderLength));
|
||||||
Slots = new Dictionary<string, Session.Slot>();
|
Slots = new Dictionary<string, Session.Slot>();
|
||||||
foreach (var s in slots)
|
foreach (var s in slots)
|
||||||
{
|
{
|
||||||
@@ -133,7 +133,7 @@ namespace OpenRA.Network
|
|||||||
Slots.Add(slot.PlayerReference, slot);
|
Slots.Add(slot.PlayerReference, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
var slotClients = MiniYaml.FromString(rs.ReadString(Encoding.UTF8, Connection.MaxOrderLength));
|
var slotClients = MiniYaml.FromString(rs.ReadLengthPrefixedString(Encoding.UTF8, Connection.MaxOrderLength));
|
||||||
SlotClients = new Dictionary<string, SlotClient>();
|
SlotClients = new Dictionary<string, SlotClient>();
|
||||||
foreach (var s in slotClients)
|
foreach (var s in slotClients)
|
||||||
{
|
{
|
||||||
@@ -144,7 +144,7 @@ namespace OpenRA.Network
|
|||||||
if (rs.Position != traitDataOffset || rs.ReadInt32() != TraitDataMarker)
|
if (rs.Position != traitDataOffset || rs.ReadInt32() != TraitDataMarker)
|
||||||
throw new InvalidDataException("Invalid orasav file");
|
throw new InvalidDataException("Invalid orasav file");
|
||||||
|
|
||||||
var traitData = MiniYaml.FromString(rs.ReadString(Encoding.UTF8, Connection.MaxOrderLength));
|
var traitData = MiniYaml.FromString(rs.ReadLengthPrefixedString(Encoding.UTF8, Connection.MaxOrderLength));
|
||||||
foreach (var td in traitData)
|
foreach (var td in traitData)
|
||||||
TraitData.Add(Exts.ParseInt32Invariant(td.Key), td.Value);
|
TraitData.Add(Exts.ParseInt32Invariant(td.Key), td.Value);
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ namespace OpenRA.Network
|
|||||||
ordersStream.Write(data.Length + 8);
|
ordersStream.Write(data.Length + 8);
|
||||||
ordersStream.Write(frame);
|
ordersStream.Write(frame);
|
||||||
ordersStream.Write(clientSlot);
|
ordersStream.Write(clientSlot);
|
||||||
ordersStream.WriteArray(data);
|
ordersStream.Write(data);
|
||||||
LastOrdersFrame = frame;
|
LastOrdersFrame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,17 +294,17 @@ namespace OpenRA.Network
|
|||||||
file.Write(lastSyncPacket, 0, Order.SyncHashOrderLength);
|
file.Write(lastSyncPacket, 0, Order.SyncHashOrderLength);
|
||||||
|
|
||||||
var globalSettingsNodes = new List<MiniYamlNode>() { GlobalSettings.Serialize() };
|
var globalSettingsNodes = new List<MiniYamlNode>() { GlobalSettings.Serialize() };
|
||||||
file.WriteString(Encoding.UTF8, globalSettingsNodes.WriteToString());
|
file.WriteLengthPrefixedString(Encoding.UTF8, globalSettingsNodes.WriteToString());
|
||||||
|
|
||||||
var slotNodes = Slots
|
var slotNodes = Slots
|
||||||
.Select(s => s.Value.Serialize())
|
.Select(s => s.Value.Serialize())
|
||||||
.ToList();
|
.ToList();
|
||||||
file.WriteString(Encoding.UTF8, slotNodes.WriteToString());
|
file.WriteLengthPrefixedString(Encoding.UTF8, slotNodes.WriteToString());
|
||||||
|
|
||||||
var slotClientNodes = SlotClients
|
var slotClientNodes = SlotClients
|
||||||
.Select(s => s.Value.Serialize(s.Key))
|
.Select(s => s.Value.Serialize(s.Key))
|
||||||
.ToList();
|
.ToList();
|
||||||
file.WriteString(Encoding.UTF8, slotClientNodes.WriteToString());
|
file.WriteLengthPrefixedString(Encoding.UTF8, slotClientNodes.WriteToString());
|
||||||
|
|
||||||
var traitDataOffset = file.Length;
|
var traitDataOffset = file.Length;
|
||||||
file.Write(TraitDataMarker);
|
file.Write(TraitDataMarker);
|
||||||
@@ -312,7 +312,7 @@ namespace OpenRA.Network
|
|||||||
var traitDataNodes = TraitData
|
var traitDataNodes = TraitData
|
||||||
.Select(kv => new MiniYamlNode(kv.Key.ToStringInvariant(), kv.Value))
|
.Select(kv => new MiniYamlNode(kv.Key.ToStringInvariant(), kv.Value))
|
||||||
.ToList();
|
.ToList();
|
||||||
file.WriteString(Encoding.UTF8, traitDataNodes.WriteToString());
|
file.WriteLengthPrefixedString(Encoding.UTF8, traitDataNodes.WriteToString());
|
||||||
|
|
||||||
file.Write((int)ordersStream.Length);
|
file.Write((int)ordersStream.Length);
|
||||||
file.Write((int)traitDataOffset);
|
file.Write((int)traitDataOffset);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace OpenRA.Network
|
|||||||
// the Order objects directly on the local client.
|
// the Order objects directly on the local client.
|
||||||
data = new MemoryStream();
|
data = new MemoryStream();
|
||||||
foreach (var o in orders)
|
foreach (var o in orders)
|
||||||
data.WriteArray(o.Serialize());
|
data.Write(o.Serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrderPacket(MemoryStream data)
|
public OrderPacket(MemoryStream data)
|
||||||
@@ -86,7 +86,7 @@ namespace OpenRA.Network
|
|||||||
ms.Write(data.Frame);
|
ms.Write(data.Frame);
|
||||||
ms.WriteByte((byte)OrderType.SyncHash);
|
ms.WriteByte((byte)OrderType.SyncHash);
|
||||||
ms.Write(data.SyncHash);
|
ms.Write(data.SyncHash);
|
||||||
ms.WriteArray(BitConverter.GetBytes(data.DefeatState));
|
ms.Write(data.DefeatState);
|
||||||
return ms.GetBuffer();
|
return ms.GetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ namespace OpenRA.Network
|
|||||||
var ms = new MemoryStream(14);
|
var ms = new MemoryStream(14);
|
||||||
ms.Write(0);
|
ms.Write(0);
|
||||||
ms.WriteByte((byte)OrderType.Ping);
|
ms.WriteByte((byte)OrderType.Ping);
|
||||||
ms.WriteArray(BitConverter.GetBytes(timestamp));
|
ms.Write(timestamp);
|
||||||
ms.WriteByte(queueLength);
|
ms.WriteByte(queueLength);
|
||||||
return ms.GetBuffer();
|
return ms.GetBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace OpenRA.Network
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.WriteArray(initialContent);
|
file.Write(initialContent);
|
||||||
writer = new BinaryWriter(file);
|
writer = new BinaryWriter(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ namespace OpenRA.Network
|
|||||||
{
|
{
|
||||||
var ms = new MemoryStream(4 + data.Length);
|
var ms = new MemoryStream(4 + data.Length);
|
||||||
ms.Write(frame);
|
ms.Write(frame);
|
||||||
ms.WriteArray(data);
|
ms.Write(data);
|
||||||
Receive(clientID, ms.GetBuffer());
|
Receive(clientID, ms.GetBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace OpenRA.Server
|
|||||||
ms.Write(0);
|
ms.Write(0);
|
||||||
ms.Write(0);
|
ms.Write(0);
|
||||||
ms.WriteByte((byte)OrderType.Ping);
|
ms.WriteByte((byte)OrderType.Ping);
|
||||||
ms.WriteArray(BitConverter.GetBytes(Game.RunTime));
|
ms.Write(Game.RunTime);
|
||||||
return ms.GetBuffer();
|
return ms.GetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -707,7 +707,7 @@ namespace OpenRA.Server
|
|||||||
ms.Write(data.Length + 4);
|
ms.Write(data.Length + 4);
|
||||||
ms.Write(client);
|
ms.Write(client);
|
||||||
ms.Write(frame);
|
ms.Write(frame);
|
||||||
ms.WriteArray(data);
|
ms.Write(data);
|
||||||
return ms.GetBuffer();
|
return ms.GetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,23 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
public static class StreamExts
|
public static class StreamExts
|
||||||
{
|
{
|
||||||
|
public static void ReadBytes(this Stream s, Span<byte> dest)
|
||||||
|
{
|
||||||
|
while (dest.Length > 0)
|
||||||
|
{
|
||||||
|
var bytesRead = s.Read(dest);
|
||||||
|
if (bytesRead == 0)
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
|
||||||
|
dest = dest[bytesRead..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] ReadBytes(this Stream s, int count)
|
public static byte[] ReadBytes(this Stream s, int count)
|
||||||
{
|
{
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
|
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
|
||||||
|
|
||||||
var buffer = new byte[count];
|
var buffer = new byte[count];
|
||||||
s.ReadBytes(buffer, 0, count);
|
s.ReadBytes(buffer, 0, count);
|
||||||
return buffer;
|
return buffer;
|
||||||
@@ -33,10 +46,11 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
|
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
|
||||||
|
|
||||||
while (count > 0)
|
while (count > 0)
|
||||||
{
|
{
|
||||||
int bytesRead;
|
var bytesRead = s.Read(buffer, offset, count);
|
||||||
if ((bytesRead = s.Read(buffer, offset, count)) == 0)
|
if (bytesRead == 0)
|
||||||
throw new EndOfStreamException();
|
throw new EndOfStreamException();
|
||||||
offset += bytesRead;
|
offset += bytesRead;
|
||||||
count -= bytesRead;
|
count -= bytesRead;
|
||||||
@@ -48,6 +62,7 @@ namespace OpenRA
|
|||||||
var b = s.ReadByte();
|
var b = s.ReadByte();
|
||||||
if (b == -1)
|
if (b == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
s.Seek(-1, SeekOrigin.Current);
|
s.Seek(-1, SeekOrigin.Current);
|
||||||
return (byte)b;
|
return (byte)b;
|
||||||
}
|
}
|
||||||
@@ -57,52 +72,85 @@ namespace OpenRA
|
|||||||
var b = s.ReadByte();
|
var b = s.ReadByte();
|
||||||
if (b == -1)
|
if (b == -1)
|
||||||
throw new EndOfStreamException();
|
throw new EndOfStreamException();
|
||||||
|
|
||||||
return (byte)b;
|
return (byte)b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ushort ReadUInt16(this Stream s)
|
public static ushort ReadUInt16(this Stream s)
|
||||||
{
|
{
|
||||||
return (ushort)(s.ReadUInt8() | s.ReadUInt8() << 8);
|
Span<byte> buffer = stackalloc byte[2];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return BitConverter.ToUInt16(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static short ReadInt16(this Stream s)
|
public static short ReadInt16(this Stream s)
|
||||||
{
|
{
|
||||||
return (short)(s.ReadUInt8() | s.ReadUInt8() << 8);
|
Span<byte> buffer = stackalloc byte[2];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return BitConverter.ToInt16(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint ReadUInt32(this Stream s)
|
public static uint ReadUInt32(this Stream s)
|
||||||
{
|
{
|
||||||
return (uint)(s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24);
|
Span<byte> buffer = stackalloc byte[4];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return BitConverter.ToUInt32(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ReadInt32(this Stream s)
|
public static int ReadInt32(this Stream s)
|
||||||
{
|
{
|
||||||
return s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24;
|
Span<byte> buffer = stackalloc byte[4];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return BitConverter.ToInt32(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Write(this Stream s, int value)
|
public static void Write(this Stream s, int value)
|
||||||
{
|
{
|
||||||
s.WriteArray(BitConverter.GetBytes(value));
|
Span<byte> buffer = stackalloc byte[4];
|
||||||
|
BitConverter.TryWriteBytes(buffer, value);
|
||||||
|
s.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this Stream s, long value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[8];
|
||||||
|
BitConverter.TryWriteBytes(buffer, value);
|
||||||
|
s.Write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this Stream s, ulong value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[8];
|
||||||
|
BitConverter.TryWriteBytes(buffer, value);
|
||||||
|
s.Write(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Write(this Stream s, float value)
|
public static void Write(this Stream s, float value)
|
||||||
{
|
{
|
||||||
s.WriteArray(BitConverter.GetBytes(value));
|
Span<byte> buffer = stackalloc byte[4];
|
||||||
|
BitConverter.TryWriteBytes(buffer, value);
|
||||||
|
s.Write(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float ReadFloat(this Stream s)
|
public static float ReadSingle(this Stream s)
|
||||||
{
|
{
|
||||||
return BitConverter.ToSingle(s.ReadBytes(4), 0);
|
Span<byte> buffer = stackalloc byte[4];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return BitConverter.ToSingle(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double ReadDouble(this Stream s)
|
public static double ReadDouble(this Stream s)
|
||||||
{
|
{
|
||||||
return BitConverter.ToDouble(s.ReadBytes(8), 0);
|
Span<byte> buffer = stackalloc byte[8];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return BitConverter.ToDouble(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadASCII(this Stream s, int length)
|
public static string ReadASCII(this Stream s, int length)
|
||||||
{
|
{
|
||||||
return new string(Encoding.ASCII.GetChars(s.ReadBytes(length)));
|
Span<byte> buffer = length < 128 ? stackalloc byte[length] : new byte[length];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return Encoding.ASCII.GetString(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadASCIIZ(this Stream s)
|
public static string ReadASCIIZ(this Stream s)
|
||||||
@@ -111,7 +159,12 @@ namespace OpenRA
|
|||||||
byte b;
|
byte b;
|
||||||
while ((b = s.ReadUInt8()) != 0)
|
while ((b = s.ReadUInt8()) != 0)
|
||||||
bytes.Add(b);
|
bytes.Add(b);
|
||||||
return new string(Encoding.ASCII.GetChars(bytes.ToArray()));
|
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
|
return Encoding.ASCII.GetString(System.Runtime.InteropServices.CollectionsMarshal.AsSpan(bytes));
|
||||||
|
#else
|
||||||
|
return Encoding.ASCII.GetString(bytes.ToArray());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadAllText(this Stream s)
|
public static string ReadAllText(this Stream s)
|
||||||
@@ -133,17 +186,11 @@ namespace OpenRA
|
|||||||
int count;
|
int count;
|
||||||
while ((count = s.Read(buffer, 0, buffer.Length)) > 0)
|
while ((count = s.Read(buffer, 0, buffer.Length)) > 0)
|
||||||
bytes.AddRange(buffer.Take(count));
|
bytes.AddRange(buffer.Take(count));
|
||||||
|
|
||||||
return bytes.ToArray();
|
return bytes.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: renamed from Write() to avoid being aliased by
|
|
||||||
// System.IO.Stream.Write(System.ReadOnlySpan) (which is not implemented in Mono)
|
|
||||||
public static void WriteArray(this Stream s, byte[] data)
|
|
||||||
{
|
|
||||||
s.Write(data, 0, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<string> ReadAllLines(this Stream s)
|
public static IEnumerable<string> ReadAllLines(this Stream s)
|
||||||
{
|
{
|
||||||
string line;
|
string line;
|
||||||
@@ -210,19 +257,24 @@ namespace OpenRA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The string is assumed to be length-prefixed, as written by WriteString()
|
/// <summary>
|
||||||
public static string ReadString(this Stream s, Encoding encoding, int maxLength)
|
/// The string is assumed to be length-prefixed, as written by <see cref="WriteLengthPrefixedString"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static string ReadLengthPrefixedString(this Stream s, Encoding encoding, int maxLength)
|
||||||
{
|
{
|
||||||
var length = s.ReadInt32();
|
var length = s.ReadInt32();
|
||||||
if (length > maxLength)
|
if (length > maxLength)
|
||||||
throw new InvalidOperationException($"The length of the string ({length}) is longer than the maximum allowed ({maxLength}).");
|
throw new InvalidOperationException($"The length of the string ({length}) is longer than the maximum allowed ({maxLength}).");
|
||||||
|
|
||||||
return encoding.GetString(s.ReadBytes(length));
|
Span<byte> buffer = length < 128 ? stackalloc byte[length] : new byte[length];
|
||||||
|
s.ReadBytes(buffer);
|
||||||
|
return encoding.GetString(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes a length-prefixed string using the specified encoding and returns
|
/// <summary>
|
||||||
// the number of bytes written.
|
/// Writes a length-prefixed string using the specified encoding and returns the number of bytes written.
|
||||||
public static int WriteString(this Stream s, Encoding encoding, string text)
|
/// </summary>
|
||||||
|
public static int WriteLengthPrefixedString(this Stream s, Encoding encoding, string text)
|
||||||
{
|
{
|
||||||
byte[] bytes;
|
byte[] bytes;
|
||||||
|
|
||||||
@@ -232,7 +284,7 @@ namespace OpenRA
|
|||||||
bytes = Array.Empty<byte>();
|
bytes = Array.Empty<byte>();
|
||||||
|
|
||||||
s.Write(bytes.Length);
|
s.Write(bytes.Length);
|
||||||
s.WriteArray(bytes);
|
s.Write(bytes);
|
||||||
|
|
||||||
return 4 + bytes.Length;
|
return 4 + bytes.Length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
{
|
{
|
||||||
readonly int outputSize;
|
readonly int outputSize;
|
||||||
int dataSize;
|
int dataSize;
|
||||||
|
byte[] inputBuffer;
|
||||||
|
byte[] outputBuffer;
|
||||||
|
|
||||||
public WestwoodCompressedAudStream(Stream stream, int outputSize, int dataSize)
|
public WestwoodCompressedAudStream(Stream stream, int outputSize, int dataSize)
|
||||||
: base(stream)
|
: base(stream)
|
||||||
@@ -167,8 +169,10 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
|
|
||||||
var chunk = AudChunk.Read(baseStream);
|
var chunk = AudChunk.Read(baseStream);
|
||||||
|
|
||||||
var input = baseStream.ReadBytes(chunk.CompressedSize);
|
var input = EnsureArraySize(ref inputBuffer, chunk.CompressedSize);
|
||||||
var output = new byte[chunk.OutputSize];
|
var output = EnsureArraySize(ref outputBuffer, chunk.OutputSize);
|
||||||
|
|
||||||
|
baseStream.ReadBytes(input);
|
||||||
WestwoodCompressedReader.DecodeWestwoodCompressedSample(input, output);
|
WestwoodCompressedReader.DecodeWestwoodCompressedSample(input, output);
|
||||||
|
|
||||||
foreach (var b in output)
|
foreach (var b in output)
|
||||||
@@ -178,6 +182,13 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
|
|
||||||
return dataSize <= 0;
|
return dataSize <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Span<byte> EnsureArraySize(ref byte[] array, int desiredSize)
|
||||||
|
{
|
||||||
|
if (array == null || array.Length < desiredSize)
|
||||||
|
array = new byte[desiredSize];
|
||||||
|
return array.AsSpan(..desiredSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
Transforms[c + 15] = 1;
|
Transforms[c + 15] = 1;
|
||||||
|
|
||||||
for (var k = 0; k < 12; k++)
|
for (var k = 0; k < 12; k++)
|
||||||
Transforms[c + ids[k]] = s.ReadFloat();
|
Transforms[c + ids[k]] = s.ReadSingle();
|
||||||
|
|
||||||
Array.Copy(Transforms, 16 * (LimbCount * j + i), testMatrix, 0, 16);
|
Array.Copy(Transforms, 16 * (LimbCount * j + i), testMatrix, 0, 16);
|
||||||
if (Util.MatrixInverse(testMatrix) == null)
|
if (Util.MatrixInverse(testMatrix) == null)
|
||||||
|
|||||||
@@ -199,16 +199,16 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
else if (AudioChannels == 1)
|
else if (AudioChannels == 1)
|
||||||
{
|
{
|
||||||
var rawAudio = stream.ReadBytes((int)length);
|
var rawAudio = stream.ReadBytes((int)length);
|
||||||
audio1.WriteArray(rawAudio);
|
audio1.Write(rawAudio);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var rawAudio = stream.ReadBytes((int)length / 2);
|
var rawAudio = stream.ReadBytes((int)length / 2);
|
||||||
audio1.WriteArray(rawAudio);
|
audio1.Write(rawAudio);
|
||||||
rawAudio = stream.ReadBytes((int)length / 2);
|
rawAudio = stream.ReadBytes((int)length / 2);
|
||||||
audio2.WriteArray(rawAudio);
|
audio2.Write(rawAudio);
|
||||||
if (length % 2 != 0)
|
if (length % 2 != 0)
|
||||||
stream.ReadBytes(2);
|
stream.Position += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
compressed = type == "SND2";
|
compressed = type == "SND2";
|
||||||
@@ -216,7 +216,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
default:
|
default:
|
||||||
if (length + stream.Position > stream.Length)
|
if (length + stream.Position > stream.Length)
|
||||||
throw new NotSupportedException($"Vqa uses unknown Subtype: {type}");
|
throw new NotSupportedException($"Vqa uses unknown Subtype: {type}");
|
||||||
stream.ReadBytes((int)length);
|
stream.Position += length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Don't parse sound here.
|
// Don't parse sound here.
|
||||||
stream.ReadBytes((int)length);
|
stream.Position += length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,8 +382,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
// frame-modifier chunk
|
// frame-modifier chunk
|
||||||
case "CBP0":
|
case "CBP0":
|
||||||
case "CBPZ":
|
case "CBPZ":
|
||||||
var bytes = s.ReadBytes(subchunkLength);
|
s.ReadBytes(cbp, chunkBufferOffset, subchunkLength);
|
||||||
bytes.CopyTo(cbp, chunkBufferOffset);
|
|
||||||
chunkBufferOffset += subchunkLength;
|
chunkBufferOffset += subchunkLength;
|
||||||
currentChunkBuffer++;
|
currentChunkBuffer++;
|
||||||
cbpIsCompressed = type == "CBPZ";
|
cbpIsCompressed = type == "CBPZ";
|
||||||
|
|||||||
@@ -141,12 +141,12 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
|||||||
{
|
{
|
||||||
limbDataOffset[i] = s.ReadUInt32();
|
limbDataOffset[i] = s.ReadUInt32();
|
||||||
s.Seek(8, SeekOrigin.Current);
|
s.Seek(8, SeekOrigin.Current);
|
||||||
Limbs[i].Scale = s.ReadFloat();
|
Limbs[i].Scale = s.ReadSingle();
|
||||||
s.Seek(48, SeekOrigin.Current);
|
s.Seek(48, SeekOrigin.Current);
|
||||||
|
|
||||||
Limbs[i].Bounds = new float[6];
|
Limbs[i].Bounds = new float[6];
|
||||||
for (var j = 0; j < 6; j++)
|
for (var j = 0; j < 6; j++)
|
||||||
Limbs[i].Bounds[j] = s.ReadFloat();
|
Limbs[i].Bounds[j] = s.ReadSingle();
|
||||||
Limbs[i].Size = s.ReadBytes(3);
|
Limbs[i].Size = s.ReadBytes(3);
|
||||||
Limbs[i].Type = (NormalType)s.ReadUInt8();
|
Limbs[i].Type = (NormalType)s.ReadUInt8();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,9 +76,7 @@ namespace OpenRA.Mods.Common.AudioLoaders
|
|||||||
{
|
{
|
||||||
readonly OggFormat format;
|
readonly OggFormat format;
|
||||||
|
|
||||||
// This buffer can be static because it can only be used by 1 instance per thread.
|
float[] conversionBuffer;
|
||||||
[ThreadStatic]
|
|
||||||
static float[] conversionBuffer;
|
|
||||||
|
|
||||||
public OggStream(OggFormat format)
|
public OggStream(OggFormat format)
|
||||||
{
|
{
|
||||||
@@ -105,13 +103,10 @@ namespace OpenRA.Mods.Common.AudioLoaders
|
|||||||
// Make sure we don't have an odd count.
|
// Make sure we don't have an odd count.
|
||||||
count -= count % format.reader.Channels;
|
count -= count % format.reader.Channels;
|
||||||
|
|
||||||
// Get the buffer, creating a new one if none exists or the existing one is too small.
|
var floatBuffer = EnsureArraySize(ref conversionBuffer, count);
|
||||||
var floatBuffer = conversionBuffer ??= new float[count];
|
|
||||||
if (floatBuffer.Length < count)
|
|
||||||
floatBuffer = conversionBuffer = new float[count];
|
|
||||||
|
|
||||||
// Let NVorbis do the actual reading.
|
// Let NVorbis do the actual reading.
|
||||||
var samples = format.reader.ReadSamples(floatBuffer, offset, count);
|
var samples = format.reader.ReadSamples(floatBuffer);
|
||||||
|
|
||||||
// Move the data back to the request buffer and convert to 16-bit signed samples for OpenAL.
|
// Move the data back to the request buffer and convert to 16-bit signed samples for OpenAL.
|
||||||
for (var i = 0; i < samples; i++)
|
for (var i = 0; i < samples; i++)
|
||||||
@@ -137,6 +132,13 @@ namespace OpenRA.Mods.Common.AudioLoaders
|
|||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Span<float> EnsureArraySize(ref float[] array, int desiredSize)
|
||||||
|
{
|
||||||
|
if (array == null || array.Length < desiredSize)
|
||||||
|
array = new float[desiredSize];
|
||||||
|
return array.AsSpan(..desiredSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.IO;
|
using System;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.FileFormats
|
namespace OpenRA.Mods.Common.FileFormats
|
||||||
{
|
{
|
||||||
@@ -56,15 +56,14 @@ namespace OpenRA.Mods.Common.FileFormats
|
|||||||
return (short)current;
|
return (short)current;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] LoadImaAdpcmSound(byte[] raw, ref int index)
|
public static byte[] LoadImaAdpcmSound(ReadOnlySpan<byte> raw, ref int index)
|
||||||
{
|
{
|
||||||
var currentSample = 0;
|
var currentSample = 0;
|
||||||
return LoadImaAdpcmSound(raw, ref index, ref currentSample);
|
return LoadImaAdpcmSound(raw, ref index, ref currentSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] LoadImaAdpcmSound(byte[] raw, ref int index, ref int currentSample)
|
public static byte[] LoadImaAdpcmSound(ReadOnlySpan<byte> raw, ref int index, ref int currentSample)
|
||||||
{
|
{
|
||||||
var s = new MemoryStream(raw);
|
|
||||||
var dataSize = raw.Length;
|
var dataSize = raw.Length;
|
||||||
var outputSize = raw.Length * 4;
|
var outputSize = raw.Length * 4;
|
||||||
|
|
||||||
@@ -73,7 +72,7 @@ namespace OpenRA.Mods.Common.FileFormats
|
|||||||
|
|
||||||
while (dataSize-- > 0)
|
while (dataSize-- > 0)
|
||||||
{
|
{
|
||||||
var b = s.ReadUInt8();
|
var b = raw[offset / 4];
|
||||||
|
|
||||||
var t = DecodeImaAdpcmSample(b, ref index, ref currentSample);
|
var t = DecodeImaAdpcmSample(b, ref index, ref currentSample);
|
||||||
output[offset++] = (byte)t;
|
output[offset++] = (byte)t;
|
||||||
|
|||||||
@@ -67,12 +67,12 @@ namespace OpenRA.Mods.Common.FileFormats
|
|||||||
blockAlign = s.ReadInt16();
|
blockAlign = s.ReadInt16();
|
||||||
sampleBits = s.ReadInt16();
|
sampleBits = s.ReadInt16();
|
||||||
lengthInSeconds = (float)(s.Length * 8) / (channels * sampleRate * sampleBits);
|
lengthInSeconds = (float)(s.Length * 8) / (channels * sampleRate * sampleBits);
|
||||||
s.ReadBytes(fmtChunkSize - 16);
|
s.Position += fmtChunkSize - 16;
|
||||||
break;
|
break;
|
||||||
case "fact":
|
case "fact":
|
||||||
var chunkSize = s.ReadInt32();
|
var chunkSize = s.ReadInt32();
|
||||||
uncompressedSize = s.ReadInt32();
|
uncompressedSize = s.ReadInt32();
|
||||||
s.ReadBytes(chunkSize - 4);
|
s.Position += chunkSize - 4;
|
||||||
break;
|
break;
|
||||||
case "data":
|
case "data":
|
||||||
dataSize = s.ReadInt32();
|
dataSize = s.ReadInt32();
|
||||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.FileFormats
|
|||||||
case "LIST":
|
case "LIST":
|
||||||
case "cue ":
|
case "cue ":
|
||||||
var listCueChunkSize = s.ReadInt32();
|
var listCueChunkSize = s.ReadInt32();
|
||||||
s.ReadBytes(listCueChunkSize);
|
s.Position += listCueChunkSize;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s.Position = s.Length; // Skip to end of stream
|
s.Position = s.Length; // Skip to end of stream
|
||||||
@@ -156,12 +156,13 @@ namespace OpenRA.Mods.Common.FileFormats
|
|||||||
|
|
||||||
// Decode and output remaining data in this block
|
// Decode and output remaining data in this block
|
||||||
var blockOffset = 0;
|
var blockOffset = 0;
|
||||||
|
Span<byte> chunk = stackalloc byte[4];
|
||||||
while (blockOffset < blockDataSize)
|
while (blockOffset < blockDataSize)
|
||||||
{
|
{
|
||||||
for (var c = 0; c < channels; c++)
|
for (var c = 0; c < channels; c++)
|
||||||
{
|
{
|
||||||
// Decode 4 bytes (to 16 bytes of output) per channel
|
// Decode 4 bytes (to 16 bytes of output) per channel
|
||||||
var chunk = baseStream.ReadBytes(4);
|
baseStream.ReadBytes(chunk);
|
||||||
var decoded = ImaAdpcmReader.LoadImaAdpcmSound(chunk, ref index[c], ref predictor[c]);
|
var decoded = ImaAdpcmReader.LoadImaAdpcmSound(chunk, ref index[c], ref predictor[c]);
|
||||||
|
|
||||||
// Interleave output, one sample per channel
|
// Interleave output, one sample per channel
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ namespace OpenRA.Mods.Common.FileFormats
|
|||||||
static readonly int[] AudWsStepTable2 = { -2, -1, 0, 1 };
|
static readonly int[] AudWsStepTable2 = { -2, -1, 0, 1 };
|
||||||
static readonly int[] AudWsStepTable4 = { -9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8 };
|
static readonly int[] AudWsStepTable4 = { -9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8 };
|
||||||
|
|
||||||
public static void DecodeWestwoodCompressedSample(byte[] input, byte[] output)
|
public static void DecodeWestwoodCompressedSample(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
if (input.Length == output.Length)
|
if (input.Length == output.Length)
|
||||||
{
|
{
|
||||||
Array.Copy(input, output, output.Length);
|
input.CopyTo(output);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace OpenRA.Mods.Common.FileSystem
|
|||||||
var dirName = s.ReadASCII(nameLength);
|
var dirName = s.ReadASCII(nameLength);
|
||||||
|
|
||||||
// Skip to the end of the chunk
|
// Skip to the end of the chunk
|
||||||
s.ReadBytes(chunkSize - nameLength - 6);
|
s.Position += chunkSize - nameLength - 6;
|
||||||
directories.Add(dirName, fileCount);
|
directories.Add(dirName, fileCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user