forked from Challengermode/CoreRcon
/
Extensions.cs
176 lines (156 loc) · 6.51 KB
/
Extensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CoreRCON
{
internal static class Extensions
{
// Trick VS into thinking this is a resolved task
internal static void Forget(this Task task)
{
}
/// <summary>
/// Step through a byte array and read a null-terminated string.
/// </summary>
/// <param name="bytes">Byte array.</param>
/// <param name="start">Offset to start reading from.</param>
/// <param name="i">Offset variable to move to the end of the string.</param>
/// <returns>UTF-8 encoded string.</returns>
public static string ReadNullTerminatedString(this byte[] bytes, int start, ref int i)
{
int end = Array.IndexOf(bytes, (byte)0, start);
if (end < 0) throw new ArgumentOutOfRangeException("Byte array does not appear to contain a null byte to stop reading a string at.");
i = end + 1;
return Encoding.UTF8.GetString(bytes, start, end - start);
}
public static List<string> ReadNullTerminatedStringArray(this byte[] bytes, int start, ref int i)
{
var result = new List<string>();
var byteindex = start;
while (bytes[byteindex] != 0x00)
{
result.Add(ReadNullTerminatedString(bytes, byteindex, ref byteindex));
}
i = byteindex + 1;
return result;
}
public static Dictionary<string, string> ReadNullTerminatedStringDictionary(this byte[] bytes, int start, ref int i)
{
var result = new Dictionary<string, string>();
var byteindex = start;
while (bytes[byteindex] != 0x00)
{
result.Add(ReadNullTerminatedString(bytes, byteindex, ref byteindex), ReadNullTerminatedString(bytes, byteindex, ref byteindex));
}
i = byteindex + 1;
return result;
}
/// <summary>
/// Read a short from a byte array and update the offset.
/// </summary>
/// <param name="bytes">Byte array.</param>
/// <param name="start">Offset to start reading from.</param>
/// <param name="i">Offset variable to move to the end of the string.</param>
public static short ReadShort(this byte[] bytes, int start, ref int i)
{
i += 2;
return BitConverter.ToInt16(bytes, start);
}
/// <summary>
/// Read a float from a byte array and update the offset.
/// </summary>
/// <param name="bytes">Byte array.</param>
/// <param name="start">Offset to start reading from.</param>
/// <param name="i">Offset variable to move to the end of the string.</param>
public static float ReadFloat(this byte[] bytes, int start, ref int i)
{
i += 4;
return BitConverter.ToSingle(bytes, start);
}
/// <summary>
/// Truncate a string to a maximum length.
/// </summary>
/// <param name="str">String to truncate.</param>
/// <param name="maxLength">Maximum length of the string.</param>
/// <returns>Truncated string with ellipses, or the original string.</returns>
internal static string Truncate(this string str, int maxLength)
{
return str?.Length <= maxLength
? str
: str.Substring(0, maxLength - 3) + "...";
}
/// <summary>
/// Receives a block of memory asyncronosly
/// </summary>
/// <param name="socket">Socket to receive from</param>
/// <param name="memory">Memory segment to receive to</param>
/// <param name="socketFlags">Flags for socket</param>
/// <returns>Awaitable task resolving to the number of bytes received</returns>
public static Task<int> ReceiveAsync(this Socket socket, Memory<byte> memory, SocketFlags socketFlags)
{
var arraySegment = GetArray(memory);
return SocketTaskExtensions.ReceiveAsync(socket, arraySegment, socketFlags);
}
public static string GetString(this Encoding encoding, ReadOnlyMemory<byte> memory)
{
var arraySegment = GetArray(memory);
return encoding.GetString(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
}
private static ArraySegment<byte> GetArray(Memory<byte> memory)
{
return GetArray((ReadOnlyMemory<byte>)memory);
}
private static ArraySegment<byte> GetArray(ReadOnlyMemory<byte> memory)
{
if (!MemoryMarshal.TryGetArray(memory, out var result))
{
throw new InvalidOperationException("Buffer backed by array was expected");
}
return result;
}
// See https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/Scenarios/Infrastructure/TaskExtensions.cs
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan? timeout)
{
if (timeout == null || timeout == TimeSpan.Zero) return await task;
using (var cts = new CancellationTokenSource())
{
var delayTask = Task.Delay(timeout.Value, cts.Token);
var resultTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
if (resultTask == delayTask)
{
// Operation cancelled
throw new TimeoutException();
}
cts.Cancel();
return await task.ConfigureAwait(false);
}
}
public static async Task TimeoutAfter(this Task task, TimeSpan? timeout)
{
if (timeout == null || timeout == TimeSpan.Zero)
{
await task;
return;
}
using (var cts = new CancellationTokenSource())
{
var delayTask = Task.Delay(timeout.Value, cts.Token);
var resultTask = await Task.WhenAny(task, delayTask);
if (resultTask == delayTask)
{
throw new TimeoutException();
}
else
{
// Cancel the timer task so that it does not fire
cts.Cancel();
}
await task;
}
}
}
}