using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Runtime.InteropServices; using System.Security; using System.Threading; using System.Threading.Tasks; using System.Web; using CryptoExchange.Net.Logging; using CryptoExchange.Net.Objects; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace CryptoExchange.Net { /// /// Helper methods /// public static class ExtensionMethods { /// /// Add a parameter /// /// /// /// public static void AddParameter(this Dictionary parameters, string key, string value) { parameters.Add(key, value); } /// /// Add a parameter /// /// /// /// public static void AddParameter(this Dictionary parameters, string key, object value) { parameters.Add(key, value); } /// /// Add an optional parameter. Not added if value is null /// /// /// /// public static void AddOptionalParameter(this Dictionary parameters, string key, object? value) { if(value != null) parameters.Add(key, value); } /// /// Add an optional parameter. Not added if value is null /// /// /// /// public static void AddOptionalParameter(this Dictionary parameters, string key, string? value) { if (value != null) parameters.Add(key, value); } /// /// Create a query string of the specified parameters /// /// The parameters to use /// Whether or not the values should be url encoded /// How to serialize array parameters /// public static string CreateParamString(this Dictionary parameters, bool urlEncodeValues, ArrayParametersSerialization serializationType) { var uriString = ""; var arraysParameters = parameters.Where(p => p.Value.GetType().IsArray).ToList(); foreach (var arrayEntry in arraysParameters) { if(serializationType == ArrayParametersSerialization.Array) uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={v}"))}&"; else { var array = (Array)arrayEntry.Value; uriString += string.Join("&", array.OfType().Select(a => $"{arrayEntry.Key}={Uri.EscapeDataString(a.ToString())}")); uriString += "&"; } } uriString += $"{string.Join("&", parameters.Where(p => !p.Value.GetType().IsArray).Select(s => $"{s.Key}={(urlEncodeValues ? Uri.EscapeDataString(s.Value.ToString()) : s.Value)}"))}"; uriString = uriString.TrimEnd('&'); return uriString; } /// /// Get the string the secure string is representing /// /// The source secure string /// public static string GetString(this SecureString source) { lock (source) { string result; var length = source.Length; var pointer = IntPtr.Zero; var chars = new char[length]; try { pointer = Marshal.SecureStringToBSTR(source); Marshal.Copy(pointer, chars, 0, length); result = string.Join("", chars); } finally { if (pointer != IntPtr.Zero) { Marshal.ZeroFreeBSTR(pointer); } } return result; } } /// /// Create a secure string from a string /// /// /// public static SecureString ToSecureString(this string source) { var secureString = new SecureString(); foreach (var c in source) secureString.AppendChar(c); secureString.MakeReadOnly(); return secureString; } /// /// Wait one async /// /// /// /// /// public static async Task WaitOneAsync(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken) { RegisteredWaitHandle? registeredHandle = null; CancellationTokenRegistration tokenRegistration = default; try { var tcs = new TaskCompletionSource(); registeredHandle = ThreadPool.RegisterWaitForSingleObject( handle, (state, timedOut) => ((TaskCompletionSource)state).TrySetResult(!timedOut), tcs, millisecondsTimeout, true); tokenRegistration = cancellationToken.Register( state => ((TaskCompletionSource)state).TrySetCanceled(), tcs); return await tcs.Task.ConfigureAwait(false); } finally { registeredHandle?.Unregister(null); tokenRegistration.Dispose(); } } /// /// Wait one async /// /// /// /// public static Task WaitOneAsync(this WaitHandle handle, TimeSpan timeout) { return handle.WaitOneAsync((int)timeout.TotalMilliseconds, CancellationToken.None); } /// /// String to JToken /// /// /// /// public static JToken? ToJToken(this string stringData, Log? log = null) { if (string.IsNullOrEmpty(stringData)) return null; try { return JToken.Parse(stringData); } catch (JsonReaderException jre) { var info = $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Data: {stringData}"; log?.Write(LogVerbosity.Error, info); if (log == null) Debug.WriteLine(info); return null; } catch (JsonSerializationException jse) { var info = $"Deserialize JsonSerializationException: {jse.Message}. Data: {stringData}"; log?.Write(LogVerbosity.Error, info); if (log == null) Debug.WriteLine(info); return null; } } /// /// Validates an int is one of the allowed values /// /// Value of the int /// Name of the parameter /// Allowed values public static void ValidateIntValues(this int value, string argumentName, params int[] allowedValues) { if (!allowedValues.Contains(value)) throw new ArgumentException( $"{value} not allowed for parameter {argumentName}, allowed values: {string.Join(", ", allowedValues)}", argumentName); } /// /// Validates an int is between two values /// /// The value of the int /// Name of the parameter /// Min value /// Max value public static void ValidateIntBetween(this int value, string argumentName, int minValue, int maxValue) { if (value < minValue || value > maxValue) throw new ArgumentException( $"{value} not allowed for parameter {argumentName}, min: {minValue}, max: {maxValue}", argumentName); } /// /// Validates a string is not null or empty /// /// The value of the string /// Name of the parameter public static void ValidateNotNull(this string value, string argumentName) { if (string.IsNullOrEmpty(value)) throw new ArgumentException($"No value provided for parameter {argumentName}", argumentName); } /// /// Validates an object is not null /// /// The value of the object /// Name of the parameter public static void ValidateNotNull(this object value, string argumentName) { if (value == null) throw new ArgumentException($"No value provided for parameter {argumentName}", argumentName); } /// /// Validates a list is not null or empty /// /// The value of the object /// Name of the parameter public static void ValidateNotNull(this IEnumerable value, string argumentName) { if (value == null || !value.Any()) throw new ArgumentException($"No values provided for parameter {argumentName}", argumentName); } } }