Kit.Core/LibExternal/Npgsql/Util/PGUtil.cs

219 lines
7.3 KiB
C#

using Npgsql.Internal;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Npgsql.Util;
static class Statics
{
#if DEBUG
internal static bool LegacyTimestampBehavior;
internal static bool DisableDateTimeInfinityConversions;
#else
internal static readonly bool LegacyTimestampBehavior;
internal static readonly bool DisableDateTimeInfinityConversions;
#endif
static Statics()
{
LegacyTimestampBehavior = AppContext.TryGetSwitch("Npgsql.EnableLegacyTimestampBehavior", out var enabled) && enabled;
DisableDateTimeInfinityConversions = AppContext.TryGetSwitch("Npgsql.DisableDateTimeInfinityConversions", out enabled) && enabled;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static T Expect<T>(IBackendMessage msg, NpgsqlConnector connector)
{
if (msg is T asT)
return asT;
throw connector.Break(
new NpgsqlException($"Received backend message {msg.Code} while expecting {typeof(T).Name}. " +
"Please file a bug."));
}
internal static DeferDisposable Defer(Action action) => new(action);
internal static DeferDisposable<T> Defer<T>(Action<T> action, T arg) => new(action, arg);
internal static DeferDisposable<T1, T2> Defer<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2) => new(action, arg1, arg2);
// internal static AsyncDeferDisposable DeferAsync(Func<ValueTask> func) => new AsyncDeferDisposable(func);
internal static AsyncDeferDisposable DeferAsync(Func<Task> func) => new(func);
internal readonly struct DeferDisposable : IDisposable
{
readonly Action _action;
public DeferDisposable(Action action) => _action = action;
public void Dispose() => _action();
}
internal readonly struct DeferDisposable<T> : IDisposable
{
readonly Action<T> _action;
readonly T _arg;
public DeferDisposable(Action<T> action, T arg)
{
_action = action;
_arg = arg;
}
public void Dispose() => _action(_arg);
}
internal readonly struct DeferDisposable<T1, T2> : IDisposable
{
readonly Action<T1, T2> _action;
readonly T1 _arg1;
readonly T2 _arg2;
public DeferDisposable(Action<T1, T2> action, T1 arg1, T2 arg2)
{
_action = action;
_arg1 = arg1;
_arg2 = arg2;
}
public void Dispose() => _action(_arg1, _arg2);
}
internal readonly struct AsyncDeferDisposable : IAsyncDisposable
{
readonly Func<Task> _func;
public AsyncDeferDisposable(Func<Task> func) => _func = func;
public async ValueTask DisposeAsync() => await _func();
}
}
// ReSharper disable once InconsistentNaming
static class PGUtil
{
internal static readonly UTF8Encoding UTF8Encoding = new(false, true);
internal static readonly UTF8Encoding RelaxedUTF8Encoding = new(false, false);
internal const int BitsInInt = sizeof(int) * 8;
internal static void ValidateBackendMessageCode(BackendMessageCode code)
{
switch (code)
{
case BackendMessageCode.AuthenticationRequest:
case BackendMessageCode.BackendKeyData:
case BackendMessageCode.BindComplete:
case BackendMessageCode.CloseComplete:
case BackendMessageCode.CommandComplete:
case BackendMessageCode.CopyData:
case BackendMessageCode.CopyDone:
case BackendMessageCode.CopyBothResponse:
case BackendMessageCode.CopyInResponse:
case BackendMessageCode.CopyOutResponse:
case BackendMessageCode.DataRow:
case BackendMessageCode.EmptyQueryResponse:
case BackendMessageCode.ErrorResponse:
case BackendMessageCode.FunctionCall:
case BackendMessageCode.FunctionCallResponse:
case BackendMessageCode.NoData:
case BackendMessageCode.NoticeResponse:
case BackendMessageCode.NotificationResponse:
case BackendMessageCode.ParameterDescription:
case BackendMessageCode.ParameterStatus:
case BackendMessageCode.ParseComplete:
case BackendMessageCode.PasswordPacket:
case BackendMessageCode.PortalSuspended:
case BackendMessageCode.ReadyForQuery:
case BackendMessageCode.RowDescription:
return;
default:
throw new NpgsqlException("Unknown message code: " + code);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int RotateShift(int val, int shift)
=> (val << shift) | (val >> (BitsInInt - shift));
internal static readonly Task<bool> TrueTask = Task.FromResult(true);
internal static readonly Task<bool> FalseTask = Task.FromResult(false);
}
enum FormatCode : short
{
Text = 0,
Binary = 1
}
static class EnumerableExtensions
{
internal static string Join(this IEnumerable<string> values, string separator)
{
return string.Join(separator, values);
}
}
static class ExceptionExtensions
{
internal static Exception UnwrapAggregate(this Exception exception)
=> exception is AggregateException agg ? agg.InnerException! : exception;
}
/// <summary>
/// Represents a timeout that will expire at some point.
/// </summary>
public readonly struct NpgsqlTimeout
{
readonly DateTime _expiration;
internal static NpgsqlTimeout Infinite = new(TimeSpan.Zero);
internal NpgsqlTimeout(TimeSpan expiration)
=> _expiration = expiration > TimeSpan.Zero
? DateTime.UtcNow + expiration
: expiration == TimeSpan.Zero
? DateTime.MaxValue
: DateTime.MinValue;
internal void Check()
{
if (HasExpired)
throw new TimeoutException();
}
internal void CheckAndApply(NpgsqlConnector connector)
{
if (!IsSet)
return;
var timeLeft = CheckAndGetTimeLeft();
// Set the remaining timeout on the read and write buffers
connector.ReadBuffer.Timeout = connector.WriteBuffer.Timeout = timeLeft;
// Note that we set UserTimeout as well, otherwise the read timeout will get overwritten in ReadMessage
// Note also that we must set the read buffer's timeout directly (above), since the SSL handshake
// reads data directly from the buffer, without going through ReadMessage.
connector.UserTimeout = (int) Math.Ceiling(timeLeft.TotalMilliseconds);
}
internal bool IsSet => _expiration != DateTime.MaxValue;
internal bool HasExpired => DateTime.UtcNow >= _expiration;
internal TimeSpan CheckAndGetTimeLeft()
{
if (!IsSet)
return Timeout.InfiniteTimeSpan;
var timeLeft = _expiration - DateTime.UtcNow;
if (timeLeft <= TimeSpan.Zero)
Check();
return timeLeft;
}
}
static class MethodInfos
{
internal static readonly ConstructorInfo InvalidCastExceptionCtor =
typeof(InvalidCastException).GetConstructor(new[] { typeof(string) })!;
internal static readonly MethodInfo StringFormat =
typeof(string).GetMethod(nameof(string.Format), new[] { typeof(string), typeof(object) })!;
internal static readonly MethodInfo ObjectGetType =
typeof(object).GetMethod(nameof(GetType), new Type[0])!;
}