IP.cs (35955B)
1 using Std.Clone; 2 using Std.Cmp; 3 using static Std.Cmp.Ordering; 4 using Std.Convert; 5 using Std.Hashing; 6 using Std.Maybe; 7 using static Std.Maybe.Maybe<Std.Cmp.Ordering>; 8 using static Std.Maybe.Maybe<uint>; 9 using static Std.Net.Functions; 10 using Std.Num; 11 using Std.Result; 12 using System; 13 using System.Runtime.InteropServices; 14 using System.Text; 15 #region Namespaces 16 namespace Std.Net { 17 #region Types 18 public interface IIP { 19 20 #region Type-level Constructors 21 #endregion 22 23 #region Instance Constructors 24 #endregion 25 26 #region Type-level Fields 27 #endregion 28 29 #region Instance Fields 30 #endregion 31 32 #region Type-level Properties 33 #endregion 34 35 #region Instance Properties 36 #endregion 37 38 #region Type-level Functions 39 #endregion 40 41 #region Instance Functions 42 public abstract IPAddr IntoIPAddr(); 43 public abstract string IntoString(); 44 public abstract bool IsBenchmarking(); 45 public abstract bool IsDocumentation(); 46 public abstract bool IsGlobal(); 47 public abstract bool IsIETFProtocol(); 48 public abstract bool IsLinkLocal(); 49 public abstract bool IsLoopback(); 50 public abstract bool IsMulticast(); 51 public abstract bool IsUnspecified(); 52 internal abstract void Sealed(); 53 #endregion 54 55 #region Operators 56 #endregion 57 58 #region Types 59 #endregion 60 } 61 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 24)] 62 public readonly struct IPAddr: IIP, ISum<IPv4Addr, IPv6Addr>, IClone<IPAddr>, IEquality<IPAddr>, IHashable, IInto<IPAddr>, IInto<byte[]>, IInto<string>, IPartialOrd<IPAddr, IPv4Addr>, IPartialOrd<IPAddr, IPv6Addr>, IPartialOrd<IPAddr, IPAddr>, ITryInto<IPv4Addr, IPVersionMismatchError>, ITryInto<IPv6Addr, IPVersionMismatchError>, ITryFrom<IPAddr, string, AddrParseError> { 63 64 #region Type-level Constructors 65 #endregion 66 67 #region Instance Constructors 68 public IPAddr() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 69 IPAddr(IPv4Addr val) { 70 71 _v6 = default; 72 (Version, _v4) = (Tag.V4, val); 73 } 74 IPAddr(IPv6Addr val) { 75 76 _v4 = default; 77 (Version, _v6) = (Tag.V6, val); 78 } 79 #endregion 80 81 #region Type-level Fields 82 #endregion 83 84 #region Instance Fields 85 [FieldOffset(0)] public readonly Tag Version; 86 [FieldOffset(8)] internal readonly IPv6Addr _v6; 87 [FieldOffset(8)] internal readonly IPv4Addr _v4; 88 #endregion 89 90 #region Type-level Properties 91 #endregion 92 93 #region Instance Properties 94 public readonly Var2 Variant => Version == Tag.V4 ? Var2.V0 : Var2.V1; 95 public readonly IPv4Addr Variant0 => Version == Tag.V4 ? _v4 : throw new InvalidOperationException($"The IPAddr variant, {Version.ToString()} is not V4!"); 96 public readonly IPv6Addr Variant1 => Version == Tag.V6 ? _v6 : throw new InvalidOperationException($"The IPAddr variant, {Version.ToString()} is not V6!"); 97 #endregion 98 99 #region Type-level Functions 100 public static IPAddr FromBEBytesIPv4(ReadOnlySpan<byte> val) => new(IPv4Addr.FromBEBytes(val)); 101 public static IPAddr FromBEBytesIPv6(ReadOnlySpan<byte> val) => new(IPv6Addr.FromBEBytes(val)); 102 public static IPAddr V4(IPv4Addr val) => new(val); 103 public static IPAddr V6(IPv6Addr val) => new(val); 104 public static Result<IPAddr, AddrParseError> TryFromSlice(ReadOnlySpan<char> val) { 105 106 var res = IPv4Addr.TryFrom(val); 107 if (res.IsOK) { return new(new IPAddr(res.Unwrap())); } 108 var res2 = IPv6Addr.TryFrom(val); 109 return res2.IsOK ? new(new IPAddr(res2.Unwrap())) : new(res2.UnwrapErr()); 110 } 111 public static Result<IPAddr, AddrParseError> TryFrom(string val) => TryFromSlice(val.AsSpan()); 112 #endregion 113 114 #region Instance Functions 115 public readonly IPAddr Clone() => this; 116 public readonly bool Equals(in IPAddr other) => this == other; 117 public override readonly bool Equals(object? _) => false; 118 public override readonly int GetHashCode() => 0; 119 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 120 public readonly IPAddr Into() => this; 121 public readonly string IntoString() => ToString(); 122 public readonly Unit IntoBEBytes(Span<byte> val) => Version == Tag.V4 ? _v4.IntoBEBytes(val) : _v6.IntoBEBytes(val); 123 public readonly byte[] IntoBEBytes() => Version == Tag.V4 ? _v4.IntoBEBytes() : _v6.IntoBEBytes(); 124 public readonly Result<IPv4Addr, IPVersionMismatchError> TryIntoIPv4() => Version == Tag.V4 ? new(_v4) : new(IPVersionMismatchError.IPVersionMismatch); 125 public readonly Result<IPv6Addr, IPVersionMismatchError> TryIntoIPv6() => Version != Tag.V4 ? new(_v6) : new(IPVersionMismatchError.IPVersionMismatch); 126 readonly string IInto<string>.Into() => IntoString(); 127 readonly byte[] IInto<byte[]>.Into() => IntoBEBytes(); 128 public readonly IPv4Addr IntoV4() => Version == Tag.V4 ? _v4 : throw new InvalidOperationException($"V6({_v6.IntoString()})"); 129 public readonly IPv6Addr IntoV46() => Version == Tag.V6 ? _v6 : throw new InvalidOperationException($"V4({_v4.IntoString()})"); 130 public readonly bool IsBenchmarking() => Version == Tag.V4 ? _v4.IsBenchmarking() : _v6.IsBenchmarking(); 131 public readonly bool IsDocumentation() => Version == Tag.V4 ? _v4.IsDocumentation() : _v6.IsDocumentation(); 132 public readonly bool IsGlobal() => Version == Tag.V4 ? _v4.IsGlobal() : _v6.IsGlobal(); 133 public readonly bool IsIETFProtocol() => Version == Tag.V4 ? _v4.IsIETFProtocol() : _v6.IsIETFProtocol(); 134 public readonly bool IsLinkLocal() => Version == Tag.V4 ? _v4.IsLinkLocal() : _v6.IsLinkLocal(); 135 public readonly bool IsLoopback() => Version == Tag.V4 ? _v4.IsLoopback() : _v6.IsLoopback(); 136 public readonly bool IsMulticast() => Version == Tag.V4 ? _v4.IsMulticast() : _v6.IsMulticast(); 137 readonly IPAddr IIP.IntoIPAddr() => this; 138 public readonly bool IsUnspecified() => Version == Tag.V4 ? _v4.IsUnspecified() : _v6.IsUnspecified(); 139 public readonly Maybe<Ordering> PartialCmp(in IPAddr other) => Version == Tag.V4 ? other.Version == Tag.V4 ? new(_v4.Cmp(other._v4)) : Maybe<Ordering>.None() : other.Version == Tag.V4 ? Maybe<Ordering>.None() : new(_v6.Cmp(in other._v6)); 140 public readonly Maybe<Ordering> PartialCmp(in IPv4Addr other) => Version == Tag.V4 ? new(_v4.Cmp(other)) : Maybe<Ordering>.None(); 141 public readonly Maybe<Ordering> PartialCmp(in IPv6Addr other) => Version == Tag.V6 ? new(_v6.Cmp(other)) : Maybe<Ordering>.None(); 142 readonly void IIP.Sealed(){} 143 public override readonly string ToString() => Version == Tag.V4 ? _v4.ToString() : _v6.ToString(); 144 readonly Result<IPAddr, Bottom> ITryInto<IPAddr, Bottom>.TryInto() => new(this); 145 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 146 readonly Result<byte[], Bottom> ITryInto<byte[], Bottom>.TryInto() => new(IntoBEBytes()); 147 readonly Result<IPv4Addr, IPVersionMismatchError> ITryInto<IPv4Addr, IPVersionMismatchError>.TryInto() => TryIntoIPv4(); 148 readonly Result<IPv6Addr, IPVersionMismatchError> ITryInto<IPv6Addr, IPVersionMismatchError>.TryInto() => TryIntoIPv6(); 149 #endregion 150 151 #region Operators 152 public static bool operator !=(IPAddr val0, IPAddr val1) => !(val0 == val1); 153 public static bool operator !=(IPAddr val0, IPv4Addr val1) => val0.Version != Tag.V4 || val0._v4 != val1; 154 public static bool operator !=(IPAddr val0, IPv6Addr val1) => val0.Version != Tag.V6 || val0._v6 != val1; 155 public static bool operator <(IPAddr val0, IPAddr val1) => val0.Version == Tag.V4 ? val1.Version == Tag.V4 && val0._v4 < val1._v4 : val1.Version == Tag.V6 && val0._v6 < val1._v6; 156 public static bool operator <(IPAddr val0, IPv4Addr val1) => val0.Version == Tag.V4 && val0._v4 < val1; 157 public static bool operator <(IPAddr val0, IPv6Addr val1) => val0.Version == Tag.V6 && val0._v6 < val1; 158 public static bool operator <=(IPAddr val0, IPAddr val1) => val0.Version == Tag.V4 ? val1.Version == Tag.V4 && val0._v4 <= val1._v4 : val1.Version == Tag.V6 && val0._v6 <= val1._v6; 159 public static bool operator <=(IPAddr val0, IPv4Addr val1) => val0.Version == Tag.V4 && val0._v4 <= val1; 160 public static bool operator <=(IPAddr val0, IPv6Addr val1) => val0.Version == Tag.V6 && val0._v6 <= val1; 161 public static bool operator ==(IPAddr val0, IPAddr val1) => val0.Version == Tag.V4 ? val1.Version == Tag.V4 && val0._v4 == val1._v4 : val1.Version == Tag.V6 && val0._v6 == val1._v6; 162 public static bool operator ==(IPAddr val0, IPv4Addr val1) => val0.Version == Tag.V4 && val0._v4 == val1; 163 public static bool operator ==(IPAddr val0, IPv6Addr val1) => val0.Version == Tag.V6 && val0._v6 == val1; 164 public static bool operator >(IPAddr val0, IPAddr val1) => val0.Version == Tag.V4 ? val1.Version == Tag.V4 && val0._v4 > val1._v4 : val1.Version == Tag.V6 && val0._v6 > val1._v6; 165 public static bool operator >(IPAddr val0, IPv4Addr val1) => val0.Version == Tag.V4 && val0._v4 > val1; 166 public static bool operator >(IPAddr val0, IPv6Addr val1) => val0.Version == Tag.V6 && val0._v6 > val1; 167 public static bool operator >=(IPAddr val0, IPAddr val1) => val0.Version == Tag.V4 ? val1.Version == Tag.V4 && val0._v4 >= val1._v4 : val1.Version == Tag.V6 && val0._v6 >= val1._v6; 168 public static bool operator >=(IPAddr val0, IPv4Addr val1) => val0.Version == Tag.V4 && val0._v4 >= val1; 169 public static bool operator >=(IPAddr val0, IPv6Addr val1) => val0.Version == Tag.V6 && val0._v6 >= val1; 170 #endregion 171 172 #region Types 173 public enum Tag: ulong { 174 V4 = ulong.MinValue, 175 V6 = ulong.MaxValue, 176 } 177 #endregion 178 } 179 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 4, Size = 4)] 180 public readonly struct IPv4Addr: IClone<IPv4Addr>, IEquality<IPv4Addr>, IInto<IPAddr>, IInto<string>, IInto<IPv4Addr>, IInto<uint>, IOrd<IPv4Addr>, IIP, IInto<byte[]>, IHashable, IPartialOrd<IPv4Addr, IPAddr>, IFrom<IPv4Addr, uint>, ITryFrom<IPv4Addr, string, AddrParseError> { 181 182 #region Type-level Constructors 183 #endregion 184 185 #region Instance Constructors 186 public IPv4Addr() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 187 public IPv4Addr(uint val) => Value = val; 188 #endregion 189 190 #region Type-level Fields 191 #endregion 192 193 #region Instance Fields 194 [FieldOffset(0)] public readonly uint Value; 195 #endregion 196 197 #region Type-level Properties 198 #endregion 199 200 #region Instance Properties 201 #endregion 202 203 #region Type-level Functions 204 public static IPv4Addr FromBEBytes(ReadOnlySpan<byte> val) => new(((uint)val[0] << 24) | ((uint)val[1] << 16) | ((uint)val[2] << 8) | val[3]); 205 public static IPv4Addr New(uint val) => new(val); 206 // Follows IETF RFC 6943 Section 3.1.1. "strict" form. 207 // Only a single pass through of val is done (i.e., each char is visited exactly once). 208 // No allocations for strings, arrays, etc. are used; instead we efficiently perform addition, multiplication, and bit shifts. 209 public static Result<IPv4Addr, AddrParseError> TryFrom(ReadOnlySpan<char> val) { 210 211 if (val.Length is < 7 or > 15) { return new(AddrParseError.InvalidLength); } 212 var (octet, octetCharCount, base256, ip) = (ulong.MinValue, uint.MinValue, 32, ulong.MinValue); 213 ulong c; 214 215 for (var i = 0; i < val.Length; i++) { 216 c = val[i]; 217 218 if (c is >= 48ul and <= 57ul) { 219 octet = (octet * 10ul) + (c - 48ul); 220 octetCharCount++; 221 } else if (c == 46ul) { 222 223 if (octetCharCount is uint.MinValue or > 3u || octet > byte.MaxValue) { 224 return new(AddrParseError.InvalidFormat); 225 } else { 226 ip |= octet << (base256 -= 8); 227 (octet, octetCharCount) = (ulong.MinValue, uint.MinValue); 228 } 229 } else { 230 return new(AddrParseError.InvalidCharacter); 231 } 232 } 233 return base256 != 8 || octetCharCount is uint.MinValue or > 3u || octet > byte.MaxValue ? new(AddrParseError.InvalidFormat) : new(new IPv4Addr((uint)(octet | ip))); 234 } 235 public static IPv4Addr From(uint val) => new(val); 236 static Result<IPv4Addr, Bottom> ITryFrom<IPv4Addr, uint, Bottom>.TryFrom(uint val) => new(From(val)); 237 public static Result<IPv4Addr, AddrParseError> TryFrom(string val) => TryFrom(val.AsSpan()); 238 #endregion 239 240 #region Instance Functions 241 public readonly IPv4Addr Clone() => this; 242 public readonly Ordering Cmp(in IPv4Addr other) => Value.CompareTo(other.Value) switch { 243 < 0 => Less, 244 > 0 => Greater, 245 0 => Equivalent, 246 }; 247 public readonly bool Equals(in IPv4Addr other) => this == other; 248 public override readonly bool Equals(object? _) => false; 249 public override readonly int GetHashCode() => 0; 250 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 251 public readonly IPv4Addr Into() => this; 252 public readonly IPAddr IntoIPAddr() => IPAddr.V4(this); 253 public readonly string IntoString() => ToString(); 254 public readonly uint IntoUint() => Value; 255 public readonly byte[] IntoBEBytes() { 256 257 var copy = Value; 258 ReadOnlySpan<byte> val; 259 unsafe { val = new(©, 4); } 260 return new byte[] { val[3], val[2], val[1], val[0] }; 261 } 262 public readonly Unit IntoBEBytes(Span<byte> bytes) { 263 264 var copy = Value; 265 ReadOnlySpan<byte> val; 266 unsafe { val = new(©, 4); } 267 (bytes[0], bytes[1], bytes[2], bytes[3]) = (val[3], val[2], val[1], val[0]); 268 return new Unit(); 269 } 270 readonly string IInto<string>.Into() => IntoString(); 271 readonly uint IInto<uint>.Into() => IntoUint(); 272 readonly IPAddr IInto<IPAddr>.Into() => IntoIPAddr(); 273 readonly byte[] IInto<byte[]>.Into() => IntoBEBytes(); 274 public readonly bool IsAMT() => Value is >= 3224682752u and < 3224683008u; 275 public readonly bool IsAS112() => Value is >= 3223307264u and < 3223307520u; 276 public readonly bool IsBenchmarking() => Value is >= 3323068416u and < 3323199488u; 277 public readonly bool IsBroadcast() => Value == uint.MaxValue; 278 public readonly bool IsIETFProtocol() => Value is >= 3221225472u and < 3221225728u; 279 public readonly bool IsDocumentation() => Value is (>= 3221225984u and < 3221226240u) or (>= 3325256704u and < 3325256960u) or (>= 3405803776u and < 3405804032u); 280 public readonly bool IsGlobal() => IsTraversalNATAnycast() || IsPortControlAnycast() || !(IsBenchmarking() || IsBroadcast() || IsDocumentation() || IsIETFProtocol() || IsLinkLocal() || IsLoopback() || IsThisNetwork() || IsPrivate() || IsReserved() || IsSharedAddress()); 281 public readonly bool IsDirectDelegationAS112() => Value is >= 3232706560u and < 3232706816u; 282 public readonly bool IsLinkLocal() => Value is >= 2851995648u and < 2852061184u; 283 public readonly bool IsLoopback() => Value is >= 2130706432u and < 2147483648u; 284 public readonly bool IsThisNetwork() => Value is >= uint.MinValue and < 16777216u; 285 public readonly bool IsMulticast() => Value is >= 3758096384u and < 4026531840u; 286 public readonly bool IsPortControlAnycast() => Value == 3221225481u; 287 public readonly bool IsPrivate() => Value is (>= 167772160u and < 184549376u) or (>= 2886729728u and < 2887778304u) or (>= 3232235520u and < 3232301056u); 288 public readonly bool IsReserved() => Value is >= 4026531840u and < uint.MaxValue; 289 public readonly bool IsSharedAddress() => Value is >= 1681915904u and < 1686110208u; 290 public readonly bool IsTraversalNATAnycast() => Value == 3221225482u; 291 public readonly bool IsUnspecified() => Value == uint.MinValue; 292 public readonly Maybe<Ordering> PartialCmp(in IPv4Addr other) => Some(Cmp(in other)); 293 public readonly Maybe<Ordering> PartialCmp(in IPAddr other) => other.Version == IPAddr.Tag.V4 ? Some(Cmp(in other._v4)) : Maybe<Ordering>.None(); 294 readonly void IIP.Sealed(){} 295 public readonly IPv6Addr ToIPv6Compatible() => new(new U128(Value, ulong.MinValue)); 296 public readonly IPv6Addr ToIPv6Mapped() => new(new U128(281470681743360ul + Value, ulong.MinValue)); 297 // Returns a string per IETF RFC 6943 Section 3.1.1. "strict" form with the added 298 // restriction that leading 0s are not used. 299 public override readonly string ToString() => $"{(Value >> 24).ToString()}.{(Value & 0xff0000u).ToString()}.{(Value & 0xff00u).ToString()}.{(Value << 24).ToString()}"; 300 readonly Result<IPv4Addr, Bottom> ITryInto<IPv4Addr, Bottom>.TryInto() => new(this); 301 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 302 readonly Result<uint, Bottom> ITryInto<uint, Bottom>.TryInto() => new(IntoUint()); 303 readonly Result<byte[], Bottom> ITryInto<byte[], Bottom>.TryInto() => new(IntoBEBytes()); 304 readonly Result<IPAddr, Bottom> ITryInto<IPAddr, Bottom>.TryInto() => new(IntoIPAddr()); 305 #endregion 306 307 #region Operators 308 public static bool operator !=(IPv4Addr val0, IPv4Addr val1) => val0.Value != val1.Value; 309 public static bool operator !=(IPv4Addr val0, IPAddr val1) => val1 != val0; 310 public static bool operator <(IPv4Addr val0, IPv4Addr val1) => val0.Value < val1.Value; 311 public static bool operator <(IPv4Addr val0, IPAddr val1) => val1 > val0; 312 public static bool operator <=(IPv4Addr val0, IPv4Addr val1) => val0.Value <= val1.Value; 313 public static bool operator <=(IPv4Addr val0, IPAddr val1) => val1 >= val0; 314 public static bool operator ==(IPv4Addr val0, IPv4Addr val1) => val0.Value == val1.Value; 315 public static bool operator ==(IPv4Addr val0, IPAddr val1) => val1 == val0; 316 public static bool operator >(IPv4Addr val0, IPv4Addr val1) => val0.Value > val1.Value; 317 public static bool operator >(IPv4Addr val0, IPAddr val1) => val1 < val0; 318 public static bool operator >=(IPv4Addr val0, IPv4Addr val1) => val0.Value >= val1.Value; 319 public static bool operator >=(IPv4Addr val0, IPAddr val1) => val1 <= val0; 320 #endregion 321 322 #region Types 323 #endregion 324 } 325 [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)] 326 public readonly struct IPv6Addr: IClone<IPv6Addr>, IEquality<IPv6Addr>, IInto<IPAddr>, IInto<string>, IInto<IPv6Addr>, IInto<U128>, IIP, IOrd<IPv6Addr>, IInto<byte[]>, IHashable, IPartialOrd<IPv6Addr, IPAddr>, ITryFrom<IPv6Addr, string, AddrParseError> { 327 328 #region Type-level Constructors 329 #endregion 330 331 #region Instance Constructors 332 public IPv6Addr() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!"); 333 public IPv6Addr(U128 val) => Value = val; 334 #endregion 335 336 #region Type-level Fields 337 #endregion 338 339 #region Instance Fields 340 [FieldOffset(0)] public readonly U128 Value; 341 #endregion 342 343 #region Type-level Properties 344 #endregion 345 346 #region Instance Properties 347 #endregion 348 349 #region Type-level Functions 350 public static IPv6Addr FromBEBytes(ReadOnlySpan<byte> val) => new(U128.FromBEBytes(val)); 351 public static IPv6Addr New(U128 val) => new(val); 352 // Follows IETF RFC 4291 Section 2.2. 353 public static Result<IPv6Addr, AddrParseError> TryFrom(ReadOnlySpan<char> val) { 354 355 if (val.Length is < 2 or > 45 || (val[0] == ':' && val[1] != ':')) { return new(AddrParseError.InvalidLength); } 356 Span<ulong> octetPairs = stackalloc ulong[8]; 357 var (octetPair, colonConsec, charCount, compressedIndex, colonIndex) = (ulong.MinValue, uint.MinValue, uint.MinValue, 0, 0); 358 ulong c; 359 var index = 0; 360 var maxIndex = 7; 361 362 for (var i = 0; i < val.Length; i++) { 363 c = val[i]; 364 365 if (c is >= 48ul and <= 57ul) { 366 octetPair = (octetPair << 4) | (c - 48ul); 367 colonConsec = uint.MinValue; 368 charCount++; 369 } else if (c is >= 97ul and <= 102ul) { 370 octetPair = (octetPair << 4) | (c - 87ul); 371 colonConsec = uint.MinValue; 372 charCount++; 373 } else if (c is >= 65ul and <= 70ul) { 374 octetPair = (octetPair << 4) | (c - 55ul); 375 colonConsec = uint.MinValue; 376 charCount++; 377 } else if (c == 58ul) { 378 379 if (charCount > 4 || index > maxIndex || colonConsec > 1u || (compressedIndex > 0 && colonConsec == 1u)) { 380 return new(AddrParseError.InvalidFormat); 381 } else if (colonConsec++ == uint.MinValue) { 382 octetPairs[index++] = octetPair; 383 } else { 384 compressedIndex = index; 385 } 386 (octetPair, charCount, colonIndex) = (ulong.MinValue, uint.MinValue, i); 387 } else if (c == 46ul) { 388 // IPv6 addresses are allowed to be terminated by an IPv4 address. 389 if (index is 0 or > 6) { return new(AddrParseError.InvalidFormat); } 390 var ipv4 = IPv4Addr.TryFrom(val[(colonIndex + 1)..]); 391 392 if (ipv4.IsErr) { 393 return new(ipv4.UnwrapErr()); 394 } else { 395 // Transform the IPv4 address into the last 2 octet-pairs. 396 var x = ipv4.Unwrap().IntoUint(); 397 (charCount, colonConsec, octetPairs[index++], octetPair) = (3, uint.MinValue, x >> 16, x & 0xffffu); 398 break; 399 } 400 } else { 401 return new(AddrParseError.InvalidCharacter); 402 } 403 } 404 if (charCount > 4 || index == 0 || index > maxIndex || colonConsec == 1u) { return new(AddrParseError.InvalidFormat); } 405 octetPairs[index++] = octetPair; 406 // We shift the octet-pairs after the double colon. 407 if (index <= maxIndex) { while (index > compressedIndex) { (octetPairs[--index], octetPairs[maxIndex--]) = (ulong.MinValue, octetPairs[index]); } } 408 return new(new IPv6Addr(new U128(octetPairs[7] | (octetPairs[6] << 16) | (octetPairs[5] << 32) | (octetPairs[4] << 48), octetPairs[3] | (octetPairs[2] << 16) | (octetPairs[1] << 32) | (octetPairs[0] << 48)))); 409 } 410 public static Result<IPv6Addr, AddrParseError> TryFrom(string val) => TryFrom(val.AsSpan()); 411 #endregion 412 413 #region Instance Functions 414 public readonly IPv6Addr Clone() => this; 415 public readonly Ordering Cmp(in IPv6Addr other) => Value.Cmp(in other.Value); 416 public readonly bool Equals(in IPv6Addr other) => this == other; 417 public override readonly bool Equals(object? _) => false; 418 public override readonly int GetHashCode() => 0; 419 public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this); 420 public readonly IPv6Addr Into() => this; 421 public readonly string IntoString() => ToString(); 422 public readonly IPAddr IntoIPAddr() => IPAddr.V6(this); 423 readonly IPAddr IInto<IPAddr>.Into() => IntoIPAddr(); 424 public readonly U128 IntoU128() => Value; 425 public readonly Unit IntoBEBytes(Span<byte> val) => Value.IntoBEBytes(val); 426 public readonly byte[] IntoBEBytes() => Value.IntoBEBytes(); 427 readonly string IInto<string>.Into() => IntoString(); 428 readonly U128 IInto<U128>.Into() => IntoU128(); 429 readonly byte[] IInto<byte[]>.Into() => IntoBEBytes(); 430 public readonly bool Is6To4() => this >= _6To4.Item0 && this < _6To4.Item1; 431 public readonly bool IsAMT() => this >= _AMT.Item0 && this < _AMT.Item1; 432 public readonly bool IsAS1122() => this >= _AS1122.Item0 && this < _AS1122.Item1; 433 public readonly bool IsBenchmarking() => this >= _benchmarking.Item0 && this < _benchmarking.Item1; 434 public readonly bool IsDirectDelegationAS112() => this >= _directDelegationAS112.Item0 && this < _directDelegationAS112.Item1; 435 public readonly bool IsDiscard() => this >= _discard.Item0 && this < _discard.Item1; 436 public readonly bool IsDocumentation() => this >= _documentation.Item0 && this < _documentation.Item1; 437 public readonly bool IsGlobal() => (this >= _IPv4IPv6TranslatedGlobal.Item0 && this < _IPv4IPv6TranslatedGlobal.Item1) || !(IsLoopback() || IsUnspecified() || IsIPv4Mapped() || IsIPv4IPv6Translated() || IsDiscard() || IsBenchmarking() || IsDocumentation() || IsLinkLocal() || IsIETFProtocol() || IsUniqueLocal() || IsTEREDO() || Is6To4()); 438 public readonly bool IsIETFProtocol() => this >= _IETFProtocol.Item0 && this < _IETFProtocol.Item1; 439 public readonly bool IsIPv4Mapped() => this >= _IPv4MappedRange.Item0 && this < _IPv4MappedRange.Item1; 440 public readonly bool IsIPv4IPv6Translated() => (this >= _IPv4IPv6Translated.Item0 && this < _IPv4IPv6Translated.Item1) || (this >= _IPv4IPv6TranslatedGlobal.Item0 && this < _IPv4IPv6TranslatedGlobal.Item1); 441 public readonly bool IsLinkLocal() => this >= _linkLocal.Item0 && this < _linkLocal.Item1; 442 public readonly bool IsLinkLocalStrict() => this >= _linkLocalStrict.Item0 && this < _linkLocalStrict.Item1; 443 public readonly bool IsLoopback() => this == _loopback; 444 public readonly bool IsMulticast() => this >= _multicast.Item0 && this <= _multicast.Item1; 445 public readonly bool IsORCHID() => this >= _ORCHID.Item0 && this < _ORCHID.Item1; 446 public readonly bool IsPortControlAnycast() => this == _portControlAnycast; 447 public readonly bool IsTEREDO() => this >= _TEREDO.Item0 && this < _TEREDO.Item1; 448 public readonly bool IsTraversalRelaysNATAnycast() => this == _traversalRelaysNATAnycast; 449 public readonly bool IsUniqueLocal() => this >= _uniqueLocal.Item0 && this < _uniqueLocal.Item1; 450 public readonly bool IsUnspecified() => this == _unspecified; 451 public readonly Maybe<Ordering> PartialCmp(in IPv6Addr other) => Some(Cmp(in other)); 452 public readonly Maybe<Ordering> PartialCmp(in IPAddr other) => other.Version == IPAddr.Tag.V6 ? Some(Cmp(in other._v6)) : Maybe<Ordering>.None(); 453 readonly void IIP.Sealed(){} 454 public readonly Maybe<IPv4Addr> ToIPv4() => Value <= new U128(uint.MaxValue, ulong.MinValue) ? new(new IPv4Addr((uint)Value)) : ToIPv4Mapped(); 455 public readonly Maybe<IPv4Addr> ToIPv4Mapped() => IsIPv4Mapped() ? new(new IPv4Addr((uint)((ulong)Value - 281470681743360ul))) : Maybe<IPv4Addr>.None(); 456 // Returns a string conforming to IETF RFC 5952 Section 4. 457 public override readonly string ToString() { 458 459 if (Value == U128.MinValue) { return "::"; } 460 Span<byte> val = stackalloc byte[16]; 461 _ = IntoBEBytes(val); 462 var fstSkippedOctetPair = -1; 463 var fstMaxSkippedOctetPair = -1; 464 var skipCounter = 0; 465 var maxSkip = 0; 466 467 for (var i = 0; i < val.Length; i += 2) { 468 469 if (val[i] + val[i + 1] == byte.MinValue) { 470 if (skipCounter == 0) { fstSkippedOctetPair = i; } 471 skipCounter++; 472 } else { 473 474 if (skipCounter > maxSkip && skipCounter > 1) { 475 fstMaxSkippedOctetPair = fstSkippedOctetPair; 476 maxSkip = skipCounter; 477 } 478 skipCounter = 0; 479 } 480 } 481 482 if (skipCounter > maxSkip && skipCounter > 1) { 483 fstMaxSkippedOctetPair = fstSkippedOctetPair; 484 maxSkip = skipCounter; 485 } 486 487 if (maxSkip > 1) { 488 StringBuilder ip = new(31); 489 for (var i = 0; i < fstMaxSkippedOctetPair; i += 2) { ip = ip.Append($"{((val[i] << 8) | val[i + 1]).ToString("x")}:"); } 490 ip = fstMaxSkippedOctetPair == 0 ? ip.Append("::") : ip.Append(':'); 491 for (var i = fstMaxSkippedOctetPair + (2 * maxSkip); i < val.Length; i += 2) { ip = ip.Append($"{((val[i] << 8) | val[i + 1]).ToString("x")}:"); } 492 return fstMaxSkippedOctetPair + (2 * maxSkip) != 16 ? ip.Remove(ip.Length - 1, 1).ToString() : ip.ToString(); 493 } else { 494 return $"{((val[0] << 8) | val[1]).ToString("x")}:{((val[2] << 8) | val[3]).ToString("x")}:{((val[4] << 8) | val[5]).ToString("x")}:{((val[6] << 8) | val[7]).ToString("x")}:{((val[8] << 8) | val[9]).ToString("x")}:{((val[10] << 8) | val[11]).ToString("x")}:{((val[12] << 8) | val[13]).ToString("x")}:{((val[14] << 8) | val[15]).ToString("x")}"; 495 } 496 } 497 readonly Result<IPv6Addr, Bottom> ITryInto<IPv6Addr, Bottom>.TryInto() => new(this); 498 readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString()); 499 readonly Result<IPAddr, Bottom> ITryInto<IPAddr, Bottom>.TryInto() => new(IntoIPAddr()); 500 readonly Result<U128, Bottom> ITryInto<U128, Bottom>.TryInto() => new(IntoU128()); 501 readonly Result<byte[], Bottom> ITryInto<byte[], Bottom>.TryInto() => new(IntoBEBytes()); 502 #endregion 503 504 #region Operators 505 public static bool operator !=(IPv6Addr val0, IPv6Addr val1) => val0.Value != val1.Value; 506 public static bool operator !=(IPv6Addr val0, IPAddr val1) => val1 != val0; 507 public static bool operator <(IPv6Addr val0, IPv6Addr val1) => val0.Value < val1.Value; 508 public static bool operator <(IPv6Addr val0, IPAddr val1) => val1 > val0; 509 public static bool operator <=(IPv6Addr val0, IPv6Addr val1) => val0.Value <= val1.Value; 510 public static bool operator <=(IPv6Addr val0, IPAddr val1) => val1 >= val0; 511 public static bool operator ==(IPv6Addr val0, IPv6Addr val1) => val0.Value == val1.Value; 512 public static bool operator ==(IPv6Addr val0, IPAddr val1) => val1 == val0; 513 public static bool operator >(IPv6Addr val0, IPv6Addr val1) => val0.Value > val1.Value; 514 public static bool operator >(IPv6Addr val0, IPAddr val1) => val1 < val0; 515 public static bool operator >=(IPv6Addr val0, IPv6Addr val1) => val0.Value >= val1.Value; 516 public static bool operator >=(IPv6Addr val0, IPAddr val1) => val1 <= val0; 517 #endregion 518 519 #region Types 520 #endregion 521 } 522 static class Functions { 523 524 #region Type-level Constructors 525 #endregion 526 527 #region Instance Constructors 528 #endregion 529 530 #region Type-level Fields 531 internal static readonly IPv6Addr _unspecified = new(U128.MinValue); 532 internal static readonly IPv6Addr _loopback = new(new U128(0x1ul, ulong.MinValue)); 533 internal static readonly IPv6Addr _portControlAnycast = new(new U128(0x1ul, 0x2001000100000000ul)); 534 internal static readonly IPv6Addr _traversalRelaysNATAnycast = new(new U128(0x2ul, 0x2001000100000000ul)); 535 internal static readonly Prod<IPv6Addr, IPv6Addr> _6To4 = new(new IPv6Addr(new U128(ulong.MinValue, 0x2002000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2003000000000000ul))); 536 internal static readonly Prod<IPv6Addr, IPv6Addr> _IPv4MappedRange = new(new IPv6Addr(new U128(0xffff00000000ul, ulong.MinValue)), new IPv6Addr(new U128(0x1000000000000ul, ulong.MinValue))); 537 internal static readonly Prod<IPv6Addr, IPv6Addr> _IPv4IPv6Translated = new(new IPv6Addr(new U128(ulong.MinValue, 0x64ff9b00010000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x64ff9b00020000ul))); 538 internal static readonly Prod<IPv6Addr, IPv6Addr> _IPv4IPv6TranslatedGlobal = new(new IPv6Addr(new U128(ulong.MinValue, 0x64ff9b00000000ul)), new IPv6Addr(new U128(0x100000000ul, 0x64ff9b00000000ul))); 539 internal static readonly Prod<IPv6Addr, IPv6Addr> _discard = new(new IPv6Addr(new U128(ulong.MinValue, 0x100000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x100000000000001ul))); 540 internal static readonly Prod<IPv6Addr, IPv6Addr> _benchmarking = new(new IPv6Addr(new U128(ulong.MinValue, 0x2001000200000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2001000200010000ul))); 541 internal static readonly Prod<IPv6Addr, IPv6Addr> _documentation = new(new IPv6Addr(new U128(ulong.MinValue, 0x20010db800000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x20010db800010000ul))); 542 internal static readonly Prod<IPv6Addr, IPv6Addr> _linkLocal = new(new IPv6Addr(new U128(ulong.MinValue, 0xfe80000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0xfec0000000000000ul))); 543 internal static readonly Prod<IPv6Addr, IPv6Addr> _linkLocalStrict = new(new IPv6Addr(new U128(ulong.MinValue, 0xfe80000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0xfe80000000000001ul))); 544 internal static readonly Prod<IPv6Addr, IPv6Addr> _IETFProtocol = new(new IPv6Addr(new U128(ulong.MinValue, 0x2001000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2001020000000000ul))); 545 internal static readonly Prod<IPv6Addr, IPv6Addr> _uniqueLocal = new(new IPv6Addr(new U128(ulong.MinValue, 0xfc00000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0xfe00000000000000ul))); 546 internal static readonly Prod<IPv6Addr, IPv6Addr> _TEREDO = new(new IPv6Addr(new U128(ulong.MinValue, 0x2001000000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2001000100000000ul))); 547 internal static readonly Prod<IPv6Addr, IPv6Addr> _AMT = new(new IPv6Addr(new U128(ulong.MinValue, 0x2001000300000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2001000400000000ul))); 548 internal static readonly Prod<IPv6Addr, IPv6Addr> _AS1122 = new(new IPv6Addr(new U128(ulong.MinValue, 0x2001000401120000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2001000401130000ul))); 549 internal static readonly Prod<IPv6Addr, IPv6Addr> _ORCHID = new(new IPv6Addr(new U128(ulong.MinValue, 0x2001002000000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2001003000000000ul))); 550 internal static readonly Prod<IPv6Addr, IPv6Addr> _directDelegationAS112 = new(new IPv6Addr(new U128(ulong.MinValue, 0x2620004f80000000ul)), new IPv6Addr(new U128(ulong.MinValue, 0x2620004f80010000ul))); 551 internal static readonly Prod<IPv6Addr, IPv6Addr> _multicast = new(new IPv6Addr(new U128(ulong.MinValue, 0xff00000000000000ul)), new IPv6Addr(U128.MaxValue)); 552 #endregion 553 554 #region Instance Fields 555 #endregion 556 557 #region Type-level Properties 558 #endregion 559 560 #region Instance Properties 561 #endregion 562 563 #region Type-level Functions 564 #endregion 565 566 #region Instance Functions 567 #endregion 568 569 #region Operators 570 #endregion 571 572 #region Types 573 #endregion 574 } 575 #endregion 576 577 #region Namespaces 578 #endregion 579 } 580 #endregion