X Tutup
using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; #pragma warning disable 1591 // ReSharper disable once CheckNamespace namespace NpgsqlTypes; /// /// Represents a PostgreSQL point type. /// /// /// See https://www.postgresql.org/docs/current/static/datatype-geometric.html /// public struct NpgsqlPoint : IEquatable { public double X { get; set; } public double Y { get; set; } public NpgsqlPoint(double x, double y) : this() { X = x; Y = y; } // ReSharper disable CompareOfFloatsByEqualityOperator public bool Equals(NpgsqlPoint other) => X == other.X && Y == other.Y; // ReSharper restore CompareOfFloatsByEqualityOperator public override bool Equals(object? obj) => obj is NpgsqlPoint point && Equals(point); public static bool operator ==(NpgsqlPoint x, NpgsqlPoint y) => x.Equals(y); public static bool operator !=(NpgsqlPoint x, NpgsqlPoint y) => !(x == y); public override int GetHashCode() => HashCode.Combine(X, Y); public override string ToString() => string.Format(CultureInfo.InvariantCulture, "({0},{1})", X, Y); } /// /// Represents a PostgreSQL line type. /// /// /// See https://www.postgresql.org/docs/current/static/datatype-geometric.html /// public struct NpgsqlLine : IEquatable { public double A { get; set; } public double B { get; set; } public double C { get; set; } public NpgsqlLine(double a, double b, double c) : this() { A = a; B = b; C = c; } public override string ToString() => string.Format(CultureInfo.InvariantCulture, "{{{0},{1},{2}}}", A, B, C); public override int GetHashCode() => HashCode.Combine(A, B, C); public bool Equals(NpgsqlLine other) => A == other.A && B == other.B && C == other.C; public override bool Equals(object? obj) => obj is NpgsqlLine line && Equals(line); public static bool operator ==(NpgsqlLine x, NpgsqlLine y) => x.Equals(y); public static bool operator !=(NpgsqlLine x, NpgsqlLine y) => !(x == y); } /// /// Represents a PostgreSQL Line Segment type. /// public struct NpgsqlLSeg : IEquatable { public NpgsqlPoint Start { get; set; } public NpgsqlPoint End { get; set; } public NpgsqlLSeg(NpgsqlPoint start, NpgsqlPoint end) : this() { Start = start; End = end; } public NpgsqlLSeg(double startx, double starty, double endx, double endy) : this() { Start = new NpgsqlPoint(startx, starty); End = new NpgsqlPoint(endx, endy); } public override string ToString() => string.Format(CultureInfo.InvariantCulture, "[{0},{1}]", Start, End); public override int GetHashCode() => HashCode.Combine(Start.X, Start.Y, End.X, End.Y); public bool Equals(NpgsqlLSeg other) => Start == other.Start && End == other.End; public override bool Equals(object? obj) => obj is NpgsqlLSeg seg && Equals(seg); public static bool operator ==(NpgsqlLSeg x, NpgsqlLSeg y) => x.Equals(y); public static bool operator !=(NpgsqlLSeg x, NpgsqlLSeg y) => !(x == y); } /// /// Represents a PostgreSQL box type. /// /// /// See https://www.postgresql.org/docs/current/static/datatype-geometric.html /// public struct NpgsqlBox : IEquatable { NpgsqlPoint _upperRight; public NpgsqlPoint UpperRight { get => _upperRight; set { _upperRight = value; NormalizeBox(); } } NpgsqlPoint _lowerLeft; public NpgsqlPoint LowerLeft { get => _lowerLeft; set { _lowerLeft = value; NormalizeBox(); } } public NpgsqlBox(NpgsqlPoint upperRight, NpgsqlPoint lowerLeft) : this() { _upperRight = upperRight; _lowerLeft = lowerLeft; NormalizeBox(); } public NpgsqlBox(double top, double right, double bottom, double left) : this(new NpgsqlPoint(right, top), new NpgsqlPoint(left, bottom)) { } public double Left => LowerLeft.X; public double Right => UpperRight.X; public double Bottom => LowerLeft.Y; public double Top => UpperRight.Y; public double Width => Right - Left; public double Height => Top - Bottom; public bool IsEmpty => Width == 0 || Height == 0; public bool Equals(NpgsqlBox other) => UpperRight == other.UpperRight && LowerLeft == other.LowerLeft; public override bool Equals(object? obj) => obj is NpgsqlBox box && Equals(box); public static bool operator ==(NpgsqlBox x, NpgsqlBox y) => x.Equals(y); public static bool operator !=(NpgsqlBox x, NpgsqlBox y) => !(x == y); public override string ToString() => string.Format(CultureInfo.InvariantCulture, "{0},{1}", UpperRight, LowerLeft); public override int GetHashCode() => HashCode.Combine(Top, Right, Bottom, LowerLeft); // Swaps corners for isomorphic boxes, to mirror postgres behavior. // See: https://github.com/postgres/postgres/blob/af2324fabf0020e464b0268be9ef03e8f46ed84b/src/backend/utils/adt/geo_ops.c#L435-L447 void NormalizeBox() { if (_upperRight.X < _lowerLeft.X) (_upperRight.X, _lowerLeft.X) = (_lowerLeft.X, _upperRight.X); if (_upperRight.Y < _lowerLeft.Y) (_upperRight.Y, _lowerLeft.Y) = (_lowerLeft.Y, _upperRight.Y); } } /// /// Represents a PostgreSQL Path type. /// public struct NpgsqlPath : IList, IEquatable { readonly List _points; public bool Open { get; set; } public NpgsqlPath() => _points = new(); public NpgsqlPath(IEnumerable points, bool open) { _points = new List(points); Open = open; } public NpgsqlPath(IEnumerable points) : this(points, false) {} public NpgsqlPath(params NpgsqlPoint[] points) : this(points, false) {} public NpgsqlPath(bool open) : this() { _points = new List(); Open = open; } public NpgsqlPath(int capacity, bool open) : this() { _points = new List(capacity); Open = open; } public NpgsqlPath(int capacity) : this(capacity, false) {} public NpgsqlPoint this[int index] { get => _points[index]; set => _points[index] = value; } public int Capacity => _points.Capacity; public int Count => _points.Count; public bool IsReadOnly => false; public int IndexOf(NpgsqlPoint item) => _points.IndexOf(item); public void Insert(int index, NpgsqlPoint item) => _points.Insert(index, item); public void RemoveAt(int index) => _points.RemoveAt(index); public void Add(NpgsqlPoint item) => _points.Add(item); public void Clear() => _points.Clear(); public bool Contains(NpgsqlPoint item) => _points.Contains(item); public void CopyTo(NpgsqlPoint[] array, int arrayIndex) => _points.CopyTo(array, arrayIndex); public bool Remove(NpgsqlPoint item) => _points.Remove(item); public IEnumerator GetEnumerator() => _points.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public bool Equals(NpgsqlPath other) { if (Open != other.Open || Count != other.Count) return false; if (ReferenceEquals(_points, other._points))//Short cut for shallow copies. return true; for (var i = 0; i != Count; ++i) if (this[i] != other[i]) return false; return true; } public override bool Equals(object? obj) => obj is NpgsqlPath path && Equals(path); public static bool operator ==(NpgsqlPath x, NpgsqlPath y) => x.Equals(y); public static bool operator !=(NpgsqlPath x, NpgsqlPath y) => !(x == y); public override int GetHashCode() { var hashCode = new HashCode(); hashCode.Add(Open); foreach (var point in this) { hashCode.Add(point.X); hashCode.Add(point.Y); } return hashCode.ToHashCode(); } public override string ToString() { var sb = new StringBuilder(); sb.Append(Open ? '[' : '('); int i; for (i = 0; i < _points.Count; i++) { var p = _points[i]; sb.AppendFormat(CultureInfo.InvariantCulture, "({0},{1})", p.X, p.Y); if (i < _points.Count - 1) sb.Append(","); } sb.Append(Open ? ']' : ')'); return sb.ToString(); } } /// /// Represents a PostgreSQL Polygon type. /// public readonly struct NpgsqlPolygon : IList, IEquatable { readonly List _points; public NpgsqlPolygon() => _points = new(); public NpgsqlPolygon(IEnumerable points) => _points = new List(points); public NpgsqlPolygon(params NpgsqlPoint[] points) : this((IEnumerable) points) {} public NpgsqlPolygon(int capacity) => _points = new List(capacity); public NpgsqlPoint this[int index] { get => _points[index]; set => _points[index] = value; } public int Capacity => _points.Capacity; public int Count => _points.Count; public bool IsReadOnly => false; public int IndexOf(NpgsqlPoint item) => _points.IndexOf(item); public void Insert(int index, NpgsqlPoint item) => _points.Insert(index, item); public void RemoveAt(int index) => _points.RemoveAt(index); public void Add(NpgsqlPoint item) => _points.Add(item); public void Clear() => _points.Clear(); public bool Contains(NpgsqlPoint item) => _points.Contains(item); public void CopyTo(NpgsqlPoint[] array, int arrayIndex) => _points.CopyTo(array, arrayIndex); public bool Remove(NpgsqlPoint item) => _points.Remove(item); public IEnumerator GetEnumerator() => _points.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public bool Equals(NpgsqlPolygon other) { if (Count != other.Count) return false; if (ReferenceEquals(_points, other._points)) return true; for (var i = 0; i != Count; ++i) if (this[i] != other[i]) return false; return true; } public override bool Equals(object? obj) => obj is NpgsqlPolygon polygon && Equals(polygon); public static bool operator ==(NpgsqlPolygon x, NpgsqlPolygon y) => x.Equals(y); public static bool operator !=(NpgsqlPolygon x, NpgsqlPolygon y) => !(x == y); public override int GetHashCode() { var hashCode = new HashCode(); foreach (var point in this) { hashCode.Add(point.X); hashCode.Add(point.Y); } return hashCode.ToHashCode(); } public override string ToString() { var sb = new StringBuilder(); sb.Append('('); int i; for (i = 0; i < _points.Count; i++) { var p = _points[i]; sb.AppendFormat(CultureInfo.InvariantCulture, "({0},{1})", p.X, p.Y); if (i < _points.Count - 1) { sb.Append(","); } } sb.Append(')'); return sb.ToString(); } } /// /// Represents a PostgreSQL Circle type. /// public struct NpgsqlCircle : IEquatable { public double X { get; set; } public double Y { get; set; } public double Radius { get; set; } public NpgsqlCircle(NpgsqlPoint center, double radius) : this() { X = center.X; Y = center.Y; Radius = radius; } public NpgsqlCircle(double x, double y, double radius) : this() { X = x; Y = y; Radius = radius; } public NpgsqlPoint Center { get => new(X, Y); set => (X, Y) = (value.X, value.Y); } // ReSharper disable CompareOfFloatsByEqualityOperator public bool Equals(NpgsqlCircle other) => X == other.X && Y == other.Y && Radius == other.Radius; // ReSharper restore CompareOfFloatsByEqualityOperator public override bool Equals(object? obj) => obj is NpgsqlCircle circle && Equals(circle); public override string ToString() => string.Format(CultureInfo.InvariantCulture, "<({0},{1}),{2}>", X, Y, Radius); public static bool operator ==(NpgsqlCircle x, NpgsqlCircle y) => x.Equals(y); public static bool operator !=(NpgsqlCircle x, NpgsqlCircle y) => !(x == y); public override int GetHashCode() => HashCode.Combine(X, Y, Radius); } /// /// Represents a PostgreSQL inet type, which is a combination of an IPAddress and a subnet mask. /// /// /// https://www.postgresql.org/docs/current/static/datatype-net-types.html /// public readonly record struct NpgsqlInet { public IPAddress Address { get; } public byte Netmask { get; } public NpgsqlInet(IPAddress address, byte netmask) { CheckAddressFamily(address); Address = address; Netmask = netmask; } public NpgsqlInet(IPAddress address) : this(address, (byte)(address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128)) { } public NpgsqlInet(string addr) { switch (addr.Split('/')) { case { Length: 2 } segments: (Address, Netmask) = (IPAddress.Parse(segments[0]), byte.Parse(segments[1])); break; case { Length: 1 } segments: var ipAddr = IPAddress.Parse(segments[0]); CheckAddressFamily(ipAddr); (Address, Netmask) = ( ipAddr, ipAddr.AddressFamily == AddressFamily.InterNetworkV6 ? (byte)128 : (byte)32); break; default: throw new FormatException("Invalid number of parts in CIDR specification"); } } public override string ToString() => (Address.AddressFamily == AddressFamily.InterNetwork && Netmask == 32) || (Address.AddressFamily == AddressFamily.InterNetworkV6 && Netmask == 128) ? Address.ToString() : $"{Address}/{Netmask}"; public static explicit operator IPAddress(NpgsqlInet inet) => inet.Address; public static implicit operator NpgsqlInet(IPAddress ip) => new(ip); public void Deconstruct(out IPAddress address, out byte netmask) { address = Address; netmask = Netmask; } static void CheckAddressFamily(IPAddress address) { if (address.AddressFamily != AddressFamily.InterNetwork && address.AddressFamily != AddressFamily.InterNetworkV6) throw new ArgumentException("Only IPAddress of InterNetwork or InterNetworkV6 address families are accepted", nameof(address)); } } /// /// Represents a PostgreSQL cidr type. /// /// /// https://www.postgresql.org/docs/current/static/datatype-net-types.html /// public readonly record struct NpgsqlCidr { public IPAddress Address { get; } public byte Netmask { get; } public NpgsqlCidr(IPAddress address, byte netmask) { if (address.AddressFamily != AddressFamily.InterNetwork && address.AddressFamily != AddressFamily.InterNetworkV6) throw new ArgumentException("Only IPAddress of InterNetwork or InterNetworkV6 address families are accepted", nameof(address)); Address = address; Netmask = netmask; } public NpgsqlCidr(string addr) => (Address, Netmask) = addr.Split('/') switch { { Length: 2 } segments => (IPAddress.Parse(segments[0]), byte.Parse(segments[1])), { Length: 1 } => throw new FormatException("Missing netmask"), _ => throw new FormatException("Invalid number of parts in CIDR specification") }; public static implicit operator NpgsqlInet(NpgsqlCidr cidr) => new(cidr.Address, cidr.Netmask); public static explicit operator IPAddress(NpgsqlCidr cidr) => cidr.Address; public override string ToString() => $"{Address}/{Netmask}"; public void Deconstruct(out IPAddress address, out byte netmask) { address = Address; netmask = Netmask; } } /// /// Represents a PostgreSQL tid value /// /// /// https://www.postgresql.org/docs/current/static/datatype-oid.html /// public readonly struct NpgsqlTid : IEquatable { /// /// Block number /// public uint BlockNumber { get; } /// /// Tuple index within block /// public ushort OffsetNumber { get; } public NpgsqlTid(uint blockNumber, ushort offsetNumber) { BlockNumber = blockNumber; OffsetNumber = offsetNumber; } public bool Equals(NpgsqlTid other) => BlockNumber == other.BlockNumber && OffsetNumber == other.OffsetNumber; public override bool Equals(object? o) => o is NpgsqlTid tid && Equals(tid); public override int GetHashCode() => (int)BlockNumber ^ OffsetNumber; public static bool operator ==(NpgsqlTid left, NpgsqlTid right) => left.Equals(right); public static bool operator !=(NpgsqlTid left, NpgsqlTid right) => !(left == right); public override string ToString() => $"({BlockNumber},{OffsetNumber})"; } #pragma warning restore 1591
X Tutup