#region License
// The PostgreSQL License
//
// Copyright (C) 2015 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using Npgsql;
#pragma warning disable 1591
namespace NpgsqlTypes
{
///
/// Represents a PostgreSQL point type.
///
///
/// See http://www.postgresql.org/docs/current/static/datatype-geometric.html
///
public struct NpgsqlPoint : IEquatable
{
static readonly Regex Regex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)");
public double X { get; set; }
public double Y { get; set; }
public NpgsqlPoint(double x, double y)
: this()
{
X = x;
Y = y;
}
public bool Equals(NpgsqlPoint other)
{
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
return obj is NpgsqlPoint && Equals((NpgsqlPoint) obj);
}
public static bool operator ==(NpgsqlPoint x, NpgsqlPoint y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlPoint x, NpgsqlPoint y)
{
return !(x == y);
}
public override int GetHashCode()
{
return X.GetHashCode() ^ PGUtil.RotateShift(Y.GetHashCode(), sizeof (int)/2);
}
public static NpgsqlPoint Parse(string s)
{
var m = Regex.Match(s);
if (!m.Success) {
throw new FormatException("Not a valid point: " + s);
}
return new NpgsqlPoint(Double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat));
}
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "({0},{1})", X, Y);
}
}
///
/// Represents a PostgreSQL line type.
///
///
/// See http://www.postgresql.org/docs/current/static/datatype-geometric.html
///
public struct NpgsqlLine : IEquatable
{
static readonly Regex Regex = new Regex(@"\{(-?\d+.?\d*),(-?\d+.?\d*),(-?\d+.?\d*)\}");
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 static NpgsqlLine Parse(string s)
{
var m = Regex.Match(s);
if (!m.Success) {
throw new FormatException("Not a valid line: " + s);
}
return new NpgsqlLine(
Double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)
);
}
public override String ToString()
{
return String.Format(CultureInfo.InvariantCulture, "{{{0},{1},{2}}}", A, B, C);
}
public override int GetHashCode()
{
return A.GetHashCode() * B.GetHashCode() * C.GetHashCode();
}
public bool Equals(NpgsqlLine other)
{
return A == other.A && B == other.B && C == other.C;
}
public override bool Equals(object obj)
{
return obj is NpgsqlLine && Equals((NpgsqlLine)obj);
}
public static bool operator ==(NpgsqlLine x, NpgsqlLine y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlLine x, NpgsqlLine y)
{
return !(x == y);
}
}
///
/// Represents a PostgreSQL Line Segment type.
///
public struct NpgsqlLSeg : IEquatable
{
static readonly Regex Regex = new Regex(@"\[\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)\]");
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 static NpgsqlLSeg Parse(string s)
{
var m = Regex.Match(s);
if (!m.Success) {
throw new FormatException("Not a valid line: " + s);
}
return new NpgsqlLSeg(
Double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[4].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)
);
}
public override String ToString()
{
return String.Format(CultureInfo.InvariantCulture, "[{0},{1}]", Start, End);
}
public override int GetHashCode()
{
return
Start.X.GetHashCode() ^ PGUtil.RotateShift(Start.Y.GetHashCode(), sizeof(int) / 4) ^
PGUtil.RotateShift(End.X.GetHashCode(), sizeof(int) / 2) ^ PGUtil.RotateShift(End.Y.GetHashCode(), sizeof(int) * 3 / 4);
}
public bool Equals(NpgsqlLSeg other)
{
return Start == other.Start && End == other.End;
}
public override bool Equals(object obj)
{
return obj is NpgsqlLSeg && Equals((NpgsqlLSeg)obj);
}
public static bool operator ==(NpgsqlLSeg x, NpgsqlLSeg y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlLSeg x, NpgsqlLSeg y)
{
return !(x == y);
}
}
///
/// Represents a PostgreSQL box type.
///
///
/// See http://www.postgresql.org/docs/current/static/datatype-geometric.html
///
public struct NpgsqlBox : IEquatable
{
static readonly Regex Regex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)");
public NpgsqlPoint UpperRight { get; set; }
public NpgsqlPoint LowerLeft { get; set; }
public NpgsqlBox(NpgsqlPoint upperRight, NpgsqlPoint lowerLeft) : this()
{
UpperRight = upperRight;
LowerLeft = lowerLeft;
}
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)
{
return UpperRight == other.UpperRight && LowerLeft == other.LowerLeft;
}
public override bool Equals(object obj)
{
return obj is NpgsqlBox && Equals((NpgsqlBox) obj);
}
public static bool operator ==(NpgsqlBox x, NpgsqlBox y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlBox x, NpgsqlBox y)
{
return !(x == y);
}
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "{0},{1}", UpperRight, LowerLeft);
}
public static NpgsqlBox Parse(string s)
{
var m = Regex.Match(s);
return new NpgsqlBox(
new NpgsqlPoint(Double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)),
new NpgsqlPoint(Double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[4].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))
);
}
public override int GetHashCode()
{
return
Top.GetHashCode() ^ PGUtil.RotateShift(Right.GetHashCode(), sizeof (int)/4) ^
PGUtil.RotateShift(Bottom.GetHashCode(), sizeof (int)/2) ^
PGUtil.RotateShift(LowerLeft.GetHashCode(), sizeof (int)*3/4);
}
}
///
/// Represents a PostgreSQL Path type.
///
public struct NpgsqlPath : IList, IEquatable
{
readonly List _points;
public bool Open { get; set; }
public NpgsqlPath(IEnumerable points, bool open) : this()
{
_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 { return _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)
{
return _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)
{
return _points.Contains(item);
}
public void CopyTo(NpgsqlPoint[] array, int arrayIndex)
{
_points.CopyTo(array, arrayIndex);
}
public bool Remove(NpgsqlPoint item)
{
return _points.Remove(item);
}
public IEnumerator GetEnumerator()
{
return _points.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Equals(NpgsqlPath other)
{
if (Open != other.Open || Count != other.Count)
return false;
else if(ReferenceEquals(_points, other._points))//Short cut for shallow copies.
return true;
for (int i = 0; i != Count; ++i)
{
if (this[i] != other[i])
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj != null && obj is NpgsqlPath && Equals((NpgsqlPath) obj);
}
public static bool operator ==(NpgsqlPath x, NpgsqlPath y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlPath x, NpgsqlPath y)
{
return !(x == y);
}
public override int GetHashCode()
{
int ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
foreach (NpgsqlPoint point in this)
{
//The ideal amount to shift each value is one that would evenly spread it throughout
//the resultant bytes. Using the current result % 32 is essentially using a random value
//but one that will be the same on subsequent calls.
ret ^= PGUtil.RotateShift(point.GetHashCode(), ret%sizeof (int));
}
return Open ? ret : -ret;
}
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();
}
public static NpgsqlPath Parse(string s)
{
bool open;
switch (s[0])
{
case '[':
open = true;
break;
case '(':
open = false;
break;
default:
throw new Exception("Invalid path string: " + s);
}
Contract.Assume(s[s.Length - 1] == (open ? ']' : ')'));
var result = new NpgsqlPath(open);
var i = 1;
while (true)
{
var i2 = s.IndexOf(')', i);
result.Add(NpgsqlPoint.Parse(s.Substring(i, i2 - i + 1)));
if (s[i2 + 1] != ',')
break;
i = i2 + 2;
}
return result;
}
}
///
/// Represents a PostgreSQL Polygon type.
///
public struct NpgsqlPolygon : IList, IEquatable
{
private readonly List _points;
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 { return _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)
{
return _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)
{
return _points.Contains(item);
}
public void CopyTo(NpgsqlPoint[] array, int arrayIndex)
{
_points.CopyTo(array, arrayIndex);
}
public bool Remove(NpgsqlPoint item)
{
return _points.Remove(item);
}
public IEnumerator GetEnumerator()
{
return _points.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Equals(NpgsqlPolygon other)
{
if (Count != other.Count)
return false;
if (ReferenceEquals(_points, other._points))
return true;
for (int i = 0; i != Count; ++i)
{
if (this[i] != other[i])
{
return false;
}
}
return true;
}
public override bool Equals(object obj)
{
return obj is NpgsqlPolygon && Equals((NpgsqlPolygon) obj);
}
public static bool operator ==(NpgsqlPolygon x, NpgsqlPolygon y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlPolygon x, NpgsqlPolygon y)
{
return !(x == y);
}
public override int GetHashCode()
{
int ret = 266370105;//seed with something other than zero to make paths of all zeros hash differently.
foreach (NpgsqlPoint point in this)
{
//The ideal amount to shift each value is one that would evenly spread it throughout
//the resultant bytes. Using the current result % 32 is essentially using a random value
//but one that will be the same on subsequent calls.
ret ^= PGUtil.RotateShift(point.GetHashCode(), ret%sizeof (int));
}
return ret;
}
public static NpgsqlPolygon Parse(string s)
{
var points = new List();
var i = 1;
while (true)
{
var i2 = s.IndexOf(')', i);
points.Add(NpgsqlPoint.Parse(s.Substring(i, i2 - i + 1)));
if (s[i2 + 1] != ',')
break;
i = i2 + 2;
}
return new NpgsqlPolygon(points);
}
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
{
static readonly Regex Regex = new Regex(@"<\((-?\d+.?\d*),(-?\d+.?\d*)\),(\d+.?\d*)>");
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 { return new NpgsqlPoint(X, Y); }
set
{
X = value.X;
Y = value.Y;
}
}
public bool Equals(NpgsqlCircle other)
{
return X == other.X && Y == other.Y && Radius == other.Radius;
}
public override bool Equals(object obj)
{
return obj is NpgsqlCircle && Equals((NpgsqlCircle) obj);
}
public static NpgsqlCircle Parse(string s)
{
var m = Regex.Match(s);
if (!m.Success) {
throw new FormatException("Not a valid circle: " + s);
}
return new NpgsqlCircle(
Double.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat),
Double.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)
);
}
public override String ToString()
{
return string.Format(CultureInfo.InvariantCulture, "<({0},{1}),{2}>", X, Y, Radius);
}
public static bool operator ==(NpgsqlCircle x, NpgsqlCircle y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlCircle x, NpgsqlCircle y)
{
return !(x == y);
}
public override int GetHashCode()
{
return X.GetHashCode() * Y.GetHashCode() * Radius.GetHashCode();
}
}
///
/// Represents a PostgreSQL inet type, which is a combination of an IPAddress and a
/// subnet mask.
///
///
/// http://www.postgresql.org/docs/current/static/datatype-net-types.html
///
public struct NpgsqlInet : IEquatable
{
public IPAddress Address;
public int Netmask;
public NpgsqlInet(IPAddress address, int 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));
Contract.EndContractBlock();
Address = address;
Netmask = netmask;
}
public NpgsqlInet(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));
Contract.EndContractBlock();
Address = address;
Netmask = address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128;
}
public NpgsqlInet(string addr)
{
if (addr.IndexOf('/') > 0)
{
var addrbits = addr.Split('/');
if (addrbits.GetUpperBound(0) != 1) {
throw new FormatException("Invalid number of parts in CIDR specification");
}
Address = IPAddress.Parse(addrbits[0]);
Netmask = int.Parse(addrbits[1]);
}
else
{
Address = IPAddress.Parse(addr);
Netmask = 32;
}
}
public override string ToString()
{
if ((Address.AddressFamily == AddressFamily.InterNetwork && Netmask == 32) ||
(Address.AddressFamily == AddressFamily.InterNetworkV6 && Netmask == 128))
{
return Address.ToString();
}
return $"{Address}/{Netmask}";
}
public static explicit operator IPAddress(NpgsqlInet x)
{
if (x.Netmask != 32) {
throw new InvalidCastException("Cannot cast CIDR network to address");
}
return x.Address;
}
public static implicit operator NpgsqlInet(IPAddress ipaddress)
{
return ReferenceEquals(ipaddress, null)
? default(NpgsqlInet)
: new NpgsqlInet(ipaddress);
}
public bool Equals(NpgsqlInet other)
{
return Address.Equals(other.Address) && Netmask == other.Netmask;
}
public override bool Equals(object obj)
{
return obj != null && obj is NpgsqlInet && Equals((NpgsqlInet) obj);
}
public override int GetHashCode()
{
return PGUtil.RotateShift(Address.GetHashCode(), Netmask%32);
}
public static bool operator ==(NpgsqlInet x, NpgsqlInet y)
{
return x.Equals(y);
}
public static bool operator !=(NpgsqlInet x, NpgsqlInet y)
{
return !(x == y);
}
}
}
#pragma warning restore 1591