Some AMP, some postgres(liek working DECIMAL) improvements.

This commit is contained in:
honzapatCZ 2026-08-23 22:13:06 +02:00
parent 41f046624d
commit 817c0d9d32
5 changed files with 920 additions and 9 deletions

View File

@ -60,7 +60,7 @@ namespace NejCommon.Controllers
var endpointBuilder = new MetadataEndpointBuilder(); var endpointBuilder = new MetadataEndpointBuilder();
populateMetadataMethod.Invoke(null, new object[] { context.MethodInfo, endpointBuilder }); populateMetadataMethod.Invoke(null, new object[] { context.MethodInfo, endpointBuilder });
var responseTypes = endpointBuilder.Metadata.Cast<IProducesResponseTypeMetadata>().ToList(); var responseTypes = endpointBuilder.Metadata.OfType<IProducesResponseTypeMetadata>().ToList();
if (!responseTypes.Any()) return; if (!responseTypes.Any()) return;
operation.Responses.Clear(); operation.Responses.Clear();
foreach (var responseType in responseTypes) foreach (var responseType in responseTypes)

View File

@ -1,5 +1,5 @@
/* /*
! tailwindcss v3.4.15 | MIT License | https://tailwindcss.com ! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com
*/ */
/* /*
@ -16,7 +16,7 @@
/* 2 */ /* 2 */
border-style: solid; border-style: solid;
/* 2 */ /* 2 */
border-color: #e5e7eb; border-color: currentColor;
/* 2 */ /* 2 */
} }
@ -415,7 +415,7 @@ video {
/* Make elements with the HTML hidden attribute stay hidden by default */ /* Make elements with the HTML hidden attribute stay hidden by default */
[hidden]:where(:not([hidden="until-found"])) { [hidden] {
display: none; display: none;
} }
@ -513,18 +513,38 @@ video {
--tw-backdrop-sepia: ; --tw-backdrop-sepia: ;
} }
.visible {
visibility: visible;
}
.static { .static {
position: static; position: static;
} }
.fixed {
position: fixed;
}
.absolute { .absolute {
position: absolute; position: absolute;
} }
.m-0 {
margin: 0px;
}
.ml-2 { .ml-2 {
margin-left: 0.5rem; margin-left: 0.5rem;
} }
.block {
display: block;
}
.inline-block {
display: inline-block;
}
.inline { .inline {
display: inline; display: inline;
} }
@ -553,6 +573,22 @@ video {
height: 12rem; height: 12rem;
} }
.h-10 {
height: 2.5rem;
}
.h-3 {
height: 0.75rem;
}
.h-2 {
height: 0.5rem;
}
.h-5 {
height: 1.25rem;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@ -565,10 +601,34 @@ video {
width: 12rem; width: 12rem;
} }
.w-10 {
width: 2.5rem;
}
.w-2 {
width: 0.5rem;
}
.w-5 {
width: 1.25rem;
}
.max-w-\[680px\] {
max-width: 680px;
}
.flex-grow { .flex-grow {
flex-grow: 1; flex-grow: 1;
} }
.table-fixed {
table-layout: fixed;
}
.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.flex-col { .flex-col {
flex-direction: column; flex-direction: column;
} }
@ -605,6 +665,14 @@ video {
align-self: flex-end; align-self: flex-end;
} }
.overflow-hidden {
overflow: hidden;
}
.break-all {
word-break: break-all;
}
.rounded-md { .rounded-md {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
@ -613,6 +681,26 @@ video {
border-radius: 0.25rem; border-radius: 0.25rem;
} }
.rounded-2xl {
border-radius: 1rem;
}
.rounded-xl {
border-radius: 0.75rem;
}
.rounded-lg {
border-radius: 0.5rem;
}
.rounded-full {
border-radius: 9999px;
}
.rounded-sm {
border-radius: 0.125rem;
}
.border-4 { .border-4 {
border-width: 4px; border-width: 4px;
} }
@ -621,11 +709,133 @@ video {
border-width: 1px; border-width: 1px;
} }
.border-t {
border-top-width: 1px;
}
.border-r {
border-right-width: 1px;
}
.border-b {
border-bottom-width: 1px;
}
.border-email-line {
--tw-border-opacity: 1;
border-color: rgb(41 55 71 / var(--tw-border-opacity));
}
.border-email-neon {
--tw-border-opacity: 1;
border-color: rgb(76 227 90 / var(--tw-border-opacity));
}
.border-email-line-soft {
--tw-border-opacity: 1;
border-color: rgb(27 41 54 / var(--tw-border-opacity));
}
.border-email-neon-soft {
--tw-border-opacity: 1;
border-color: rgb(21 56 32 / var(--tw-border-opacity));
}
.border-email-red-soft {
--tw-border-opacity: 1;
border-color: rgb(58 23 26 / var(--tw-border-opacity));
}
.border-email-yellow-soft {
--tw-border-opacity: 1;
border-color: rgb(59 48 18 / var(--tw-border-opacity));
}
.bg-primary { .bg-primary {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(var(--primary) / var(--tw-bg-opacity)); background-color: rgb(var(--primary) / var(--tw-bg-opacity));
} }
.bg-email-canvas {
--tw-bg-opacity: 1;
background-color: rgb(7 11 16 / var(--tw-bg-opacity));
}
.bg-email-header {
--tw-bg-opacity: 1;
background-color: rgb(8 17 28 / var(--tw-bg-opacity));
}
.bg-email-neon-soft {
--tw-bg-opacity: 1;
background-color: rgb(21 56 32 / var(--tw-bg-opacity));
}
.bg-email-surface {
--tw-bg-opacity: 1;
background-color: rgb(13 21 30 / var(--tw-bg-opacity));
}
.bg-email-surface-2 {
--tw-bg-opacity: 1;
background-color: rgb(17 28 39 / var(--tw-bg-opacity));
}
.bg-email-line-soft {
--tw-bg-opacity: 1;
background-color: rgb(27 41 54 / var(--tw-bg-opacity));
}
.bg-email-red {
--tw-bg-opacity: 1;
background-color: rgb(240 82 82 / var(--tw-bg-opacity));
}
.bg-email-neon {
--tw-bg-opacity: 1;
background-color: rgb(76 227 90 / var(--tw-bg-opacity));
}
.bg-email-blue {
--tw-bg-opacity: 1;
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
}
.bg-email-blue-2 {
--tw-bg-opacity: 1;
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
}
.bg-email-slate-bar {
--tw-bg-opacity: 1;
background-color: rgb(122 135 149 / var(--tw-bg-opacity));
}
.bg-email-purple {
--tw-bg-opacity: 1;
background-color: rgb(139 92 246 / var(--tw-bg-opacity));
}
.bg-email-yellow {
--tw-bg-opacity: 1;
background-color: rgb(251 191 36 / var(--tw-bg-opacity));
}
.bg-email-orange {
--tw-bg-opacity: 1;
background-color: rgb(249 115 22 / var(--tw-bg-opacity));
}
.bg-email-red-soft {
--tw-bg-opacity: 1;
background-color: rgb(58 23 26 / var(--tw-bg-opacity));
}
.bg-email-yellow-soft {
--tw-bg-opacity: 1;
background-color: rgb(59 48 18 / var(--tw-bg-opacity));
}
.py-2 { .py-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
@ -641,6 +851,66 @@ video {
padding-right: 2.5rem; padding-right: 2.5rem;
} }
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.py-6 {
padding-top: 1.5rem;
padding-bottom: 1.5rem;
}
.px-7 {
padding-left: 1.75rem;
padding-right: 1.75rem;
}
.px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-2\.5 {
padding-top: 0.625rem;
padding-bottom: 0.625rem;
}
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-0\.5 {
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}
.py-0 {
padding-top: 0px;
padding-bottom: 0px;
}
.py-1\.5 {
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.pt-2 { .pt-2 {
padding-top: 0.5rem; padding-top: 0.5rem;
} }
@ -665,10 +935,74 @@ video {
padding-top: 0.25rem; padding-top: 0.25rem;
} }
.pl-3 {
padding-left: 0.75rem;
}
.pt-5 {
padding-top: 1.25rem;
}
.pt-4 {
padding-top: 1rem;
}
.pr-1\.5 {
padding-right: 0.375rem;
}
.pr-1 {
padding-right: 0.25rem;
}
.pl-2 {
padding-left: 0.5rem;
}
.pl-1\.5 {
padding-left: 0.375rem;
}
.pl-1 {
padding-left: 0.25rem;
}
.pr-3 {
padding-right: 0.75rem;
}
.pl-4 {
padding-left: 1rem;
}
.pb-4 {
padding-bottom: 1rem;
}
.pt-3 {
padding-top: 0.75rem;
}
.pb-3 {
padding-bottom: 0.75rem;
}
.text-left { .text-left {
text-align: left; text-align: left;
} }
.text-center {
text-align: center;
}
.align-middle {
vertical-align: middle;
}
.font-email {
font-family: Trebuchet MS, Segoe UI, Arial, sans-serif;
}
.text-sm { .text-sm {
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
@ -699,6 +1033,46 @@ video {
line-height: 2rem; line-height: 2rem;
} }
.text-\[11px\] {
font-size: 11px;
}
.text-\[28px\] {
font-size: 28px;
}
.text-\[14px\] {
font-size: 14px;
}
.text-\[13px\] {
font-size: 13px;
}
.text-\[22px\] {
font-size: 22px;
}
.text-\[10px\] {
font-size: 10px;
}
.text-\[9px\] {
font-size: 9px;
}
.text-\[24px\] {
font-size: 24px;
}
.text-\[8px\] {
font-size: 8px;
}
.text-\[12px\] {
font-size: 12px;
}
.font-semibold { .font-semibold {
font-weight: 600; font-weight: 600;
} }
@ -707,6 +1081,46 @@ video {
font-weight: 700; font-weight: 700;
} }
.font-extrabold {
font-weight: 800;
}
.font-medium {
font-weight: 500;
}
.uppercase {
text-transform: uppercase;
}
.normal-case {
text-transform: none;
}
.leading-\[34px\] {
line-height: 34px;
}
.leading-5 {
line-height: 1.25rem;
}
.leading-4 {
line-height: 1rem;
}
.tracking-\[0\.18em\] {
letter-spacing: 0.18em;
}
.tracking-wide {
letter-spacing: 0.025em;
}
.tracking-normal {
letter-spacing: 0em;
}
.text-secondary { .text-secondary {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(var(--secondary-text) / var(--tw-text-opacity)); color: rgb(var(--secondary-text) / var(--tw-text-opacity));
@ -717,6 +1131,41 @@ video {
color: rgb(var(--primary-text) / var(--tw-text-opacity)); color: rgb(var(--primary-text) / var(--tw-text-opacity));
} }
.text-email-text {
--tw-text-opacity: 1;
color: rgb(244 247 250 / var(--tw-text-opacity));
}
.text-email-neon {
--tw-text-opacity: 1;
color: rgb(76 227 90 / var(--tw-text-opacity));
}
.text-email-muted {
--tw-text-opacity: 1;
color: rgb(160 172 184 / var(--tw-text-opacity));
}
.text-email-dim {
--tw-text-opacity: 1;
color: rgb(112 128 145 / var(--tw-text-opacity));
}
.text-email-red {
--tw-text-opacity: 1;
color: rgb(240 82 82 / var(--tw-text-opacity));
}
.text-email-yellow {
--tw-text-opacity: 1;
color: rgb(251 191 36 / var(--tw-text-opacity));
}
.text-email-purple {
--tw-text-opacity: 1;
color: rgb(139 92 246 / var(--tw-text-opacity));
}
.line-through { .line-through {
text-decoration-line: line-through; text-decoration-line: line-through;
} }
@ -725,6 +1174,11 @@ video {
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
} }
.backdrop-filter {
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
}
:root { :root {
/* /*
--primary: #3d3d3d; --primary: #3d3d3d;

View File

@ -178,6 +178,30 @@ public abstract class CommonDbContext : DbContext
return entity; return entity;
} }
public T Create<T, TParent>(TParent parentI, string navigationName, Action<T> config = null, params object[] constructorArguments) where T : class, new() where TParent : class
{
var candidates = Model
.GetEntityTypes()
.Where(x => x.ClrType == typeof(TParent))
.FirstOrDefault();
var entityT = candidates?.FindNavigation(navigationName)?.TargetEntityType;
if (entityT == null || entityT.IsOwned())
{
var childEntity = new T();
config?.Invoke(childEntity);
return childEntity;
}
var entity = this.Set<T>(entityT.Name).CreateProxy(constructorArguments);
config?.Invoke(entity);
this.Add(entity);
this.ChangeTracker.DetectChanges();
return entity;
}
public T Create<T>(Action<T> config = null, params object[] constructorArguments) public T Create<T>(Action<T> config = null, params object[] constructorArguments)
{ {
var entity = this.CreateProxy<T>(constructorArguments); var entity = this.CreateProxy<T>(constructorArguments);

View File

@ -0,0 +1,21 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using NejAccountingAPI.Models;
namespace AutoMapProperty;
public class AutoMapPropertyUtils
{
public static T Create<T>(IServiceProvider provider) where T : class
{
var appDb = provider.GetRequiredService<AppDbContext>();
return appDb.Create<T>();
}
public static T Create<T, TParent>(IServiceProvider provider, TParent parent, string name) where T : class, new() where TParent : class
{
var appDb = provider.GetRequiredService<AppDbContext>();
return appDb.Create<T, TParent>(parent, name);
}
}

View File

@ -0,0 +1,412 @@
using System.Globalization;
using System.Numerics;
using System.Text;
using Npgsql.Internal;
using Npgsql.Internal.Converters;
using Npgsql.Internal.Postgres;
namespace NejCommon.Utils;
#pragma warning disable NPG9001 // decimalype is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
public sealed class RoundingNumericTypeInfoResolverFactory
: PgTypeInfoResolverFactory
{
public override IPgTypeInfoResolver CreateResolver()
=> new Resolver();
public override IPgTypeInfoResolver? CreateArrayResolver()
=> null;
private sealed class Resolver : IPgTypeInfoResolver
{
private readonly TypeInfoMappingCollection _mappings =
CreateMappings();
public PgTypeInfo? GetTypeInfo(
Type? type,
DataTypeName? dataTypeName,
PgSerializerOptions options)
=> _mappings.Find(type, dataTypeName, options);
private static TypeInfoMappingCollection CreateMappings()
{
var mappings = new TypeInfoMappingCollection();
mappings.AddStructType<decimal>(
"numeric",
static (options, mapping, _) =>
mapping.CreateInfo(
options,
new RoundingDecimalNumericConverter()),
isDefault: true);
return mappings;
}
}
}
public sealed class RoundingDecimalNumericConverter
: PgBufferedConverter<decimal>
{
private const ushort SignPositive = 0x0000;
private const ushort SignNegative = 0x4000;
private const ushort SignNaN = 0xC000;
private const ushort SignPositiveInfinity = 0xD000;
private const ushort SignNegativeInfinity = 0xF000;
private static readonly BigInteger MaxDecimalCoefficient =
(BigInteger.One << 96) - 1;
public override bool CanConvert(
DataFormat format,
out BufferRequirements bufferRequirements)
{
// Let Npgsql buffer the entire binary numeric value before calling
// ReadCore/WriteCore.
return CanConvertBufferedDefault(format, out bufferRequirements);
}
protected override decimal ReadCore(PgReader reader)
{
// PostgreSQL numeric binary format:
//
// int16 ndigits
// int16 weight
// int16 sign
// int16 dscale
// int16 digits[ndigits]
//
// Each digit is base 10000.
var digitCount = reader.ReadInt16();
var weight = reader.ReadInt16();
var sign = reader.ReadUInt16();
// IMPORTANT:
// This is DISPLAY scale, not necessarily the number of physically
// significant fractional digits.
//
// e.g. 100.000000000000000000000000000000 may have
// dscale=30 but physically contain only the digit 100.
_ = reader.ReadInt16();
if (digitCount < 0)
throw new InvalidOperationException(
$"Invalid PostgreSQL numeric digit count: {digitCount}");
switch (sign)
{
case SignNaN:
throw new InvalidCastException(
"Numeric NaN cannot be represented as System.Decimal.");
case SignPositiveInfinity:
throw new InvalidCastException(
"Numeric Infinity cannot be represented as System.Decimal.");
case SignNegativeInfinity:
throw new InvalidCastException(
"Numeric -Infinity cannot be represented as System.Decimal.");
case not (SignPositive or SignNegative):
throw new InvalidCastException(
$"Unknown PostgreSQL numeric sign 0x{sign:X4}.");
}
if (digitCount == 0)
return decimal.Zero;
BigInteger coefficient = BigInteger.Zero;
for (var i = 0; i < digitCount; i++)
{
var digit = reader.ReadInt16();
if (digit is < 0 or >= 10000)
throw new InvalidOperationException(
$"Invalid PostgreSQL numeric digit: {digit}");
coefficient *= 10000;
coefficient += digit;
}
return ToDecimal(
coefficient,
digitCount,
weight,
sign == SignNegative);
}
private static decimal ToDecimal(
BigInteger coefficient,
int digitCount,
int weight,
bool negative)
{
if (coefficient.IsZero)
return decimal.Zero;
/*
* PostgreSQL numeric:
*
* digits are base 10000
* weight tells us the weight of the first group
*
* Therefore:
*
* coefficient * 10^(-physicalScale)
*
* gives us the actual numerical value.
*/
var physicalScale =
checked(4 * (digitCount - 1 - weight));
// Value has zeroes to the right, e.g. a positive exponent.
if (physicalScale < 0)
{
coefficient *= BigInteger.Pow(10, -physicalScale);
physicalScale = 0;
}
/*
* Strip REAL trailing zeroes first.
*
* This is the important part for values such as:
*
* 100.000000000000000000000000000000
*
* We care about the value, not PostgreSQL's nominal dscale.
*/
while (physicalScale > 0 &&
coefficient % 10 == 0)
{
coefficient /= 10;
physicalScale--;
}
/*
* System.Decimal is:
*
* 96-bit unsigned coefficient
* scale 0..28
*
* Try the highest scale we can preserve.
*
* If necessary, progressively round away fractional precision.
*/
var targetScale = Math.Min(physicalScale, 28);
for (; targetScale >= 0; targetScale--)
{
var digitsToDrop = physicalScale - targetScale;
var rounded = digitsToDrop == 0
? coefficient
: RoundToEven(coefficient, digitsToDrop);
if (rounded <= MaxDecimalCoefficient)
{
return CreateDecimal(
rounded,
targetScale,
negative);
}
}
// Even integer-scale representation doesn't fit.
// This is a REAL overflow rather than useless fractional precision.
throw new OverflowException(
"PostgreSQL numeric magnitude does not fit in System.Decimal.");
}
private static BigInteger RoundToEven(
BigInteger value,
int decimalDigitsToDrop)
{
if (decimalDigitsToDrop <= 0)
return value;
var divisor =
BigInteger.Pow(10, decimalDigitsToDrop);
var quotient =
BigInteger.DivRem(value, divisor, out var remainder);
var doubledRemainder = remainder * 2;
var comparison = doubledRemainder.CompareTo(divisor);
// MidpointRounding.ToEven
if (comparison > 0 ||
(comparison == 0 && !quotient.IsEven))
{
quotient++;
}
return quotient;
}
private static decimal CreateDecimal(
BigInteger coefficient,
int scale,
bool negative)
{
var lo = (uint)(coefficient & uint.MaxValue);
var mid = (uint)((coefficient >> 32) & uint.MaxValue);
var hi = (uint)((coefficient >> 64) & uint.MaxValue);
return new decimal(
unchecked((int)lo),
unchecked((int)mid),
unchecked((int)hi),
negative,
checked((byte)scale));
}
// ----------------------------
// Writing CLR decimal -> numeric
// ----------------------------
public override Size GetSize(
SizeContext context,
decimal value,
ref object? writeState)
{
var encoded = Encode(value);
// ndigits + weight + sign + dscale = 8 bytes
// plus 2 bytes per base-10000 digit.
return 8 + encoded.Digits.Length * 2;
}
protected override void WriteCore(
PgWriter writer,
decimal value)
{
var encoded = Encode(value);
writer.WriteInt16(
checked((short)encoded.Digits.Length));
writer.WriteInt16(encoded.Weight);
writer.WriteUInt16(encoded.Sign);
writer.WriteInt16(encoded.Scale);
foreach (var digit in encoded.Digits)
writer.WriteInt16(digit);
}
private static EncodedNumeric Encode(decimal value)
{
var bits = decimal.GetBits(value);
var negative =
(bits[3] & int.MinValue) != 0;
var scale =
(bits[3] >> 16) & 0xFF;
var coefficient =
((BigInteger)(uint)bits[2] << 64) |
((BigInteger)(uint)bits[1] << 32) |
(uint)bits[0];
if (coefficient.IsZero)
{
return new EncodedNumeric(
Array.Empty<short>(),
0,
SignPositive,
checked((short)scale));
}
var text =
coefficient.ToString(
CultureInfo.InvariantCulture);
string integerPart;
string fractionalPart;
if (scale == 0)
{
integerPart = text;
fractionalPart = "";
}
else if (text.Length <= scale)
{
integerPart = "0";
fractionalPart =
new string('0', scale - text.Length) + text;
}
else
{
integerPart = text[..^scale];
fractionalPart = text[^scale..];
}
// PostgreSQL numeric groups contain 4 decimal digits each.
var integerPadding =
(4 - integerPart.Length % 4) % 4;
integerPart =
new string('0', integerPadding) +
integerPart;
var fractionalPadding =
(4 - fractionalPart.Length % 4) % 4;
fractionalPart +=
new string('0', fractionalPadding);
var integerGroups =
integerPart.Length / 4;
var all = integerPart + fractionalPart;
var groups =
new List<short>(all.Length / 4);
for (var i = 0; i < all.Length; i += 4)
{
groups.Add(
short.Parse(
all.AsSpan(i, 4),
CultureInfo.InvariantCulture));
}
var weight = integerGroups - 1;
// PostgreSQL itself doesn't transmit unnecessary leading groups.
while (groups.Count > 0 && groups[0] == 0)
{
groups.RemoveAt(0);
weight--;
}
// Nor unnecessary trailing groups.
while (groups.Count > 0 &&
groups[^1] == 0)
{
groups.RemoveAt(groups.Count - 1);
}
if (groups.Count == 0)
weight = 0;
return new EncodedNumeric(
groups.ToArray(),
checked((short)weight),
negative
? SignNegative
: SignPositive,
checked((short)scale));
}
private sealed record EncodedNumeric(
short[] Digits,
short Weight,
ushort Sign,
short Scale);
}
#pragma warning restore NPG9001 // decimalype is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.