Skip to content

Commit b2ae668

Browse files
committed
WzBinaryReader: optimisation to take advantage of Span<T> and stackalloc (.net 8.0)
1 parent d579fe8 commit b2ae668

File tree

1 file changed

+82
-70
lines changed

1 file changed

+82
-70
lines changed

MapleLib/WzLib/Util/WzBinaryReader.cs

Lines changed: 82 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1717
using System;
1818
using System.Diagnostics;
1919
using System.IO;
20+
using System.Runtime.CompilerServices;
2021
using System.Text;
2122
using MapleLib.MapleCryptoLib;
2223
using MapleLib.PacketLib;
@@ -78,68 +79,73 @@ public string ReadStringAtOffset(long Offset, bool readByte)
7879
return ReturnString;
7980
}
8081

82+
/// <summary>
83+
/// Reads a string from the buffer
84+
/// </summary>
85+
/// <returns></returns>
8186
public override string ReadString()
8287
{
8388
sbyte smallLength = base.ReadSByte();
84-
8589
if (smallLength == 0)
86-
{
8790
return string.Empty;
88-
}
8991

9092
int length;
91-
StringBuilder retString = new StringBuilder();
9293
if (smallLength > 0) // Unicode
94+
length = smallLength == sbyte.MaxValue ? ReadInt32() : smallLength;
95+
else // ASCII
96+
length = smallLength == sbyte.MinValue ? ReadInt32() : -smallLength;
97+
98+
if (length <= 0)
99+
return string.Empty;
100+
101+
if (smallLength > 0) // Unicode
102+
return DecodeUnicode(length);
103+
else
104+
return DecodeAscii(length);
105+
}
106+
107+
/// <summary>
108+
/// Decodes unicode string
109+
/// </summary>
110+
/// <param name="length"></param>
111+
/// <returns></returns>
112+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113+
private string DecodeUnicode(int length)
114+
{
115+
Span<char> chars = length <= 1024 ? stackalloc char[length] : new char[length];
116+
ushort mask = 0xAAAA;
117+
118+
for (int i = 0; i < length; i++)
93119
{
94-
ushort mask = 0xAAAA;
95-
if (smallLength == sbyte.MaxValue)
96-
{
97-
length = ReadInt32();
98-
}
99-
else
100-
{
101-
length = (int)smallLength;
102-
}
103-
if (length <= 0)
104-
{
105-
return string.Empty;
106-
}
107-
108-
for (int i = 0; i < length; i++)
109-
{
110-
ushort encryptedChar = ReadUInt16();
111-
encryptedChar ^= mask;
112-
encryptedChar ^= (ushort)((WzKey[(i * 2 + 1)] << 8) + WzKey[(i * 2)]);
113-
retString.Append((char)encryptedChar);
114-
mask++;
115-
}
120+
ushort encryptedChar = ReadUInt16();
121+
encryptedChar ^= mask;
122+
encryptedChar ^= (ushort)((WzKey[(i * 2 + 1)] << 8) + WzKey[(i * 2)]);
123+
chars[i] = (char)encryptedChar;
124+
mask++;
116125
}
117-
else
118-
{ // ASCII
119-
byte mask = 0xAA;
120-
if (smallLength == sbyte.MinValue)
121-
{
122-
length = ReadInt32();
123-
}
124-
else
125-
{
126-
length = (int)(-smallLength);
127-
}
128-
if (length <= 0)
129-
{
130-
return string.Empty;
131-
}
126+
return new string(chars);
127+
}
132128

133-
for (int i = 0; i < length; i++)
134-
{
135-
byte encryptedChar = ReadByte();
136-
encryptedChar ^= mask;
137-
encryptedChar ^= (byte)WzKey[i];
138-
retString.Append((char)encryptedChar);
139-
mask++;
140-
}
129+
/// <summary>
130+
/// Decodes Ascii string
131+
/// </summary>
132+
/// <param name="length"></param>
133+
/// <returns></returns>
134+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
135+
private string DecodeAscii(int length)
136+
{
137+
Span<byte> bytes = length <= 1024 ? stackalloc byte[length] : new byte[length];
138+
byte mask = 0xAA;
139+
140+
for (int i = 0; i < length; i++)
141+
{
142+
byte encryptedChar = ReadByte();
143+
encryptedChar ^= mask;
144+
encryptedChar ^= (byte)WzKey[i];
145+
bytes[i] = encryptedChar;
146+
mask++;
141147
}
142-
return retString.ToString();
148+
return Encoding.ASCII.GetString(bytes);
143149
}
144150

145151
/// <summary>
@@ -153,14 +159,15 @@ public string ReadString(int length)
153159

154160
public string ReadNullTerminatedString()
155161
{
156-
StringBuilder retString = new StringBuilder();
157-
byte b = ReadByte();
158-
while (b != 0)
162+
using (var memoryStream = new MemoryStream())
159163
{
160-
retString.Append((char)b);
161-
b = ReadByte();
164+
byte b;
165+
while ((b = ReadByte()) != 0)
166+
{
167+
memoryStream.WriteByte(b);
168+
}
169+
return Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
162170
}
163-
return retString.ToString();
164171
}
165172

166173
public int ReadCompressedInt()
@@ -206,33 +213,38 @@ public long ReadOffset()
206213
return (offset + startOffset);
207214
}
208215

216+
/// <summary>
217+
/// Decrypts List.wz string without mask
218+
/// </summary>
219+
/// <param name="stringToDecrypt"></param>
220+
/// <returns></returns>
209221
public string DecryptString(char[] stringToDecrypt)
210222
{
211-
StringBuilder outputString = new StringBuilder();
223+
Span<char> outputChars = stringToDecrypt.Length <= 1024
224+
? stackalloc char[stringToDecrypt.Length]
225+
: new char[stringToDecrypt.Length];
212226

213-
int i = 0;
214-
foreach (char c in stringToDecrypt)
227+
for (int i = 0; i < stringToDecrypt.Length; i++)
215228
{
216-
outputString.Append((char)(c ^ ((char)((WzKey[i * 2 + 1] << 8) + WzKey[i * 2]))));
217-
i++;
229+
outputChars[i] = (char)(stringToDecrypt[i] ^ ((char)((WzKey[i * 2 + 1] << 8) + WzKey[i * 2])));
218230
}
219-
return outputString.ToString();
231+
232+
return new string(outputChars);
220233
}
221234

222235

223236
public string DecryptNonUnicodeString(char[] stringToDecrypt)
224237
{
225-
// Initialize the output string with the correct capacity
226-
StringBuilder outputString = new StringBuilder(stringToDecrypt.Length);
238+
Span<char> outputChars = stringToDecrypt.Length <= 1024
239+
? stackalloc char[stringToDecrypt.Length]
240+
: new char[stringToDecrypt.Length];
227241

228242
for (int i = 0; i < stringToDecrypt.Length; i++)
229243
{
230-
// Append the decrypted character to the StringBuilder object
231-
outputString.Append((char)(stringToDecrypt[i] ^ WzKey[i]));
244+
outputChars[i] = (char)(stringToDecrypt[i] ^ WzKey[i]);
232245
}
233246

234-
// Convert the StringBuilder object to a string and return it
235-
return outputString.ToString();
247+
return new string(outputChars);
236248
}
237249

238250
public string ReadStringBlock(long offset)
@@ -306,8 +318,8 @@ public void PrintHexBytes(int numberOfBytes)
306318
this.BaseStream.Position -= numberOfBytes;
307319
#endif
308320
}
309-
#endregion
310-
321+
#endregion
322+
311323
#region Overrides
312324
public override void Close()
313325
{

0 commit comments

Comments
 (0)