Skip to content

Commit 43399be

Browse files
committed
Add Core pooling providers [skip ci]
1 parent ca20d4a commit 43399be

5 files changed

Lines changed: 366 additions & 163 deletions

File tree

src/ExtensionMethods.cs

Lines changed: 12 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
using System.Linq;
55
using System.Text;
66
using System.Text.RegularExpressions;
7+
using Oxide.Pooling;
78

8-
namespace Oxide.Core
9+
namespace Oxide
910
{
1011
/// <summary>
1112
/// Useful extension methods which are added to base types
@@ -175,178 +176,26 @@ public static string ToSentence<T>(this IEnumerable<T> items)
175176
/// <param name="max"></param>
176177
/// <returns></returns>
177178
public static string Truncate(this string text, int max) => text.Length <= max ? text : text.Substring(0, max) + " ...";
178-
}
179-
}
180179

181-
namespace Oxide.Plugins
182-
{
183-
/// <summary>
184-
/// Useful extension methods which are added to base types
185-
/// </summary>
186-
public static class ExtensionMethods
187-
{
188180
/// <summary>
189-
/// Returns the last portion of a path separated by slashes
181+
/// Creates a new <see cref="HashSet{T}"/> based off a <see cref="IEnumerable{T}"/>
190182
/// </summary>
191-
public static string Basename(this string text, string extension = null)
192-
{
193-
if (extension != null)
194-
{
195-
if (extension.Equals("*.*"))
196-
{
197-
// Return the name excluding any extension
198-
Match match = Regex.Match(text, @"([^\\/]+)\.[^\.]+$");
199-
if (match.Success)
200-
{
201-
return match.Groups[1].Value;
202-
}
203-
}
204-
else
205-
{
206-
// Return the name excluding the given extension
207-
if (extension[0] == '*')
208-
{
209-
extension = extension.Substring(1);
210-
}
211-
212-
return Regex.Match(text, @"([^\\/]+)\" + extension + "+$").Groups[1].Value;
213-
}
214-
}
215-
// No extension was given or the path has no extension, return the full file name
216-
return Regex.Match(text, @"[^\\/]+$").Groups[0].Value;
217-
}
218-
219-
/// <summary>
220-
/// Checks if an array contains a specific item
221-
/// </summary>
222-
public static bool Contains<T>(this T[] array, T value)
223-
{
224-
foreach (T item in array)
225-
{
226-
if (item.Equals(value))
227-
{
228-
return true;
229-
}
230-
}
231-
232-
return false;
233-
}
234-
235-
/// <summary>
236-
/// Returns the directory portion of a path separated by slashes
237-
/// </summary>
238-
public static string Dirname(this string text) => Regex.Match(text, "(.+)[\\/][^\\/]+$").Groups[1].Value;
239-
240-
/// <summary>
241-
/// Converts PascalCase and camelCase to multiple words
242-
/// </summary>
243-
public static string Humanize(this string name) => Regex.Replace(name, @"(\B[A-Z])", " $1");
244-
245-
/// <summary>
246-
/// Checks if a string is a valid 64-bit Steam ID
247-
/// </summary>
248-
public static bool IsSteamId(this string id)
249-
{
250-
return ulong.TryParse(id, out ulong targetId) && targetId > 76561197960265728ul;
251-
}
252-
253-
/// <summary>
254-
/// Checks if a ulong is a valid 64-bit Steam ID
255-
/// </summary>
256-
public static bool IsSteamId(this ulong id) => id > 76561197960265728ul;
257-
258-
/// <summary>
259-
/// Converts a string to plain text
260-
/// </summary>
261-
/// <param name="text"></param>
183+
/// <param name="collection">The collection to copy items from</param>
184+
/// <typeparam name="T"></typeparam>
262185
/// <returns></returns>
263-
public static string Plaintext(this string text) => Formatter.ToPlaintext(text);
186+
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> collection) => new HashSet<T>(collection);
264187

265-
/// <summary>
266-
/// Converts a string into a quote safe string
267-
/// </summary>
268-
/// <param name="text"></param>
269-
/// <returns></returns>
270-
public static string QuoteSafe(this string text) => "\"" + text.Replace("\"", "\\\"").TrimEnd('\\') + "\"";
271-
272-
/// <summary>
273-
/// Converts a string into a quote safe string
274-
/// </summary>
275-
/// <param name="text"></param>
276-
/// <returns></returns>
277-
public static string Quote(this string text) => QuoteSafe(text);
278-
279-
/// <summary>
280-
/// Returns a random value from an array
281-
/// </summary>
282-
public static T Sample<T>(this T[] array) => array[Core.Random.Range(0, array.Length)];
283-
284-
/// <summary>
285-
/// Converts a string into a sanitized string for string.Format
286-
/// </summary>
287-
/// <param name="text"></param>
288-
/// <returns></returns>
289-
public static string Sanitize(this string text) => text.Replace("{", "{{").Replace("}", "}}");
290-
291-
/// <summary>
292-
/// Converts a string to Sentence case
293-
/// </summary>
294-
public static string SentenceCase(this string text)
295-
{
296-
Regex regex = new Regex(@"(^[a-z])|\.\s+(.)", RegexOptions.ExplicitCapture);
297-
return regex.Replace(text.ToLower(), s => s.Value.ToUpper());
298-
}
299-
300-
/// <summary>
301-
/// Converts a string to Title Case
302-
/// </summary>
303-
public static string TitleCase(this string text)
304-
{
305-
return CultureInfo.InstalledUICulture.TextInfo.ToTitleCase(text.Contains('_') ? text.Replace('_', ' ') : text);
306-
}
307-
308-
/// <summary>
309-
/// Converts a string to Title Case
310-
/// </summary>
311-
public static string Titleize(this string text) => TitleCase(text);
188+
#region Pooling
312189

313190
/// <summary>
314-
/// Turns an array of strings into a sentence
191+
/// Gets a <see cref="IArrayPoolProvider{T}"/> from the factory
315192
/// </summary>
316-
/// <param name="items"></param>
193+
/// <param name="factory"></param>
194+
/// <typeparam name="T"></typeparam>
317195
/// <returns></returns>
318-
public static string ToSentence<T>(this IEnumerable<T> items)
319-
{
320-
IEnumerator<T> enumerator = items.GetEnumerator();
321-
if (!enumerator.MoveNext())
322-
{
323-
return string.Empty;
324-
}
325-
326-
T firstItem = enumerator.Current;
327-
if (!enumerator.MoveNext())
328-
{
329-
return firstItem?.ToString();
330-
}
196+
public static IArrayPoolProvider<T> GetArrayProvider<T>(this IPoolFactory factory) => factory.GetProvider<T[]>() as IArrayPoolProvider<T>;
331197

332-
StringBuilder builder = new StringBuilder(firstItem?.ToString());
333-
bool moreItems = true;
334-
while (moreItems)
335-
{
336-
T item = enumerator.Current;
337-
moreItems = enumerator.MoveNext();
338-
builder.Append(moreItems ? ", " : " and ");
339-
builder.Append(item);
340-
}
341-
return builder.ToString();
342-
}
198+
#endregion
343199

344-
/// <summary>
345-
/// Shortens a string to the length specified
346-
/// </summary>
347-
/// <param name="text"></param>
348-
/// <param name="max"></param>
349-
/// <returns></returns>
350-
public static string Truncate(this string text, int max) => text.Length <= max ? text : text.Substring(0, max) + " ...";
351200
}
352201
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Oxide.Pooling
6+
{
7+
public class BaseArrayPoolProvider<T> : IArrayPoolProvider<T>
8+
{
9+
private readonly int maxSetCapacity;
10+
private readonly int maxArrayLength;
11+
12+
private readonly T[] empty;
13+
private readonly ICollection<ICollection<T[]>> pooledArrays;
14+
15+
public BaseArrayPoolProvider()
16+
{
17+
maxSetCapacity = 100;
18+
maxArrayLength = 50;
19+
// ReSharper disable once VirtualMemberCallInConstructor
20+
empty = InstantiateArray(0);
21+
22+
if (maxArrayLength > 7)
23+
{
24+
pooledArrays = new HashSet<ICollection<T[]>>();
25+
}
26+
else
27+
{
28+
pooledArrays = new List<ICollection<T[]>>(maxArrayLength - 1);
29+
}
30+
31+
for (int i = 0; i < maxArrayLength; i++)
32+
{
33+
ICollection<T[]> pool;
34+
35+
if (maxSetCapacity > 6)
36+
{
37+
pool = new HashSet<T[]>();
38+
}
39+
else
40+
{
41+
pool = new List<T[]>(maxSetCapacity);
42+
}
43+
44+
pooledArrays.Add(pool);
45+
}
46+
}
47+
48+
public T[] Take() => empty;
49+
50+
public T[] Take(int length)
51+
{
52+
if (length == 0)
53+
{
54+
return empty;
55+
}
56+
57+
if (length > maxArrayLength)
58+
{
59+
return InstantiateArray(length);
60+
}
61+
62+
if (length < 0)
63+
{
64+
throw new ArgumentOutOfRangeException(nameof(length), length, "must be at least zero");
65+
}
66+
67+
T[] item;
68+
69+
ICollection<T[]> pooled = pooledArrays.ElementAt(length - 1);
70+
71+
lock (pooled)
72+
{
73+
if (pooled.Count != 0)
74+
{
75+
item = pooled.ElementAt(0);
76+
pooled.Remove(item);
77+
}
78+
else
79+
{
80+
item = InstantiateArray(length);
81+
}
82+
}
83+
84+
OnTake(item);
85+
return item;
86+
}
87+
88+
public void Return(object item)
89+
{
90+
if (!(item is T[] array))
91+
{
92+
return;
93+
}
94+
95+
if (array.Length == 0 || array.Length > maxArrayLength)
96+
{
97+
return;
98+
}
99+
100+
if (!OnReturn(array))
101+
{
102+
return;
103+
}
104+
105+
ICollection<T[]> pooled = pooledArrays.ElementAt(array.Length - 1);
106+
107+
lock (pooled)
108+
{
109+
if (pooled.Count < maxSetCapacity)
110+
{
111+
pooled.Add(array);
112+
}
113+
}
114+
}
115+
116+
protected virtual void OnTake(T[] array)
117+
{
118+
}
119+
120+
protected virtual bool OnReturn(T[] array)
121+
{
122+
for (int i = 0; i < array.Length; i++)
123+
{
124+
array[i] = default;
125+
}
126+
127+
return true;
128+
}
129+
130+
protected virtual T[] InstantiateArray(int length) => new T[length];
131+
}
132+
}

0 commit comments

Comments
 (0)