1+ using System . Collections . Generic ;
2+ using System . Linq ;
3+ using System . Net ;
4+ using System . Numerics ;
5+ using System . Text ;
6+ using System . Text . RegularExpressions ;
7+
8+ namespace org . dmxc . wkdt . Network
9+ {
10+ [ Serializable ]
11+ public readonly struct IPv6Address : IEquatable < IPv6Address >
12+ {
13+ public static IPv6Address Empty { get => new IPv6Address ( "::" ) ; }
14+ public static IPv6Address LocalHost { get => new IPv6Address ( "::1" ) ; }
15+
16+ private static readonly Regex regex = new Regex ( @"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$" ) ;
17+
18+ public readonly BigInteger Raw ;
19+ #if NET8_0_OR_GREATER
20+ [ JsonInclude ]
21+ #endif
22+ public readonly string String ;
23+
24+ #if NET8_0_OR_GREATER
25+ [ JsonConstructor ]
26+ #endif
27+ public IPv6Address ( string @string )
28+ {
29+ if ( ! regex . Match ( @string ) . Success )
30+ throw new FormatException ( "The given string is not a IPv6Address" ) ;
31+ // Expand the IPv6 address if it uses the :: shorthand
32+ string expandedAddress = ExpandIPv6Address ( @string ) ;
33+
34+ String = @string ;
35+
36+ // Split the expanded address into its component hextets
37+ string [ ] hextets = expandedAddress . Split ( ':' ) ;
38+
39+ // Convert each hextet to its corresponding integer value and combine into a BigInteger
40+ Raw = BigInteger . Zero ;
41+ foreach ( string hextet in hextets )
42+ Raw = ( Raw << 16 ) + BigInteger . Parse ( hextet , System . Globalization . NumberStyles . HexNumber ) ;
43+
44+ }
45+ public IPv6Address ( BigInteger bigInteger ) : this ( )
46+ {
47+ Raw = bigInteger ;
48+ byte [ ] bytes = new byte [ 16 ] ;
49+ var bigBytes = bigInteger . ToByteArray ( ) ;
50+ Array . Copy ( bigBytes , bytes , bigBytes . Length ) ;
51+ String = StringFromBytes ( bytes ) ;
52+ }
53+ public IPv6Address ( byte [ ] bytes ) : this ( )
54+ {
55+ if ( bytes . Length != 16 )
56+ throw new ArgumentOutOfRangeException ( "bytes should be an array with a length of 16" ) ;
57+
58+ Raw = new BigInteger ( bytes ) ;
59+ String = StringFromBytes ( bytes ) ;
60+ }
61+ public IPv6Address ( IEnumerable < byte > enumerable ) : this ( enumerable . ToArray ( ) )
62+ {
63+ }
64+ private static string StringFromBytes ( byte [ ] bytes )
65+ {
66+ StringBuilder sb = new StringBuilder ( ) ;
67+ for ( int i = 0 ; i < bytes . Length ; i += 2 )
68+ {
69+ sb . Append ( $ "{ bytes [ i ] : x2} { bytes [ i + 1 ] : x2} ") ;
70+ if ( i < 14 )
71+ sb . Append ( ':' ) ;
72+ }
73+ var str = sb . ToString ( ) ;
74+ while ( str . Contains ( ":0" ) )
75+ str = str . Replace ( ":0" , ":" ) ;
76+ while ( str . Contains ( ":::" ) )
77+ str = str . Replace ( ":::" , "::" ) ;
78+ return str ;
79+ }
80+ private static string ExpandIPv6Address ( string ipv6Address )
81+ {
82+ if ( ipv6Address == "::" ) return "0000:0000:0000:0000:0000:0000:0000:0000" ;
83+
84+ string [ ] parts = ipv6Address . Split ( new string [ ] { "::" } , StringSplitOptions . None ) ;
85+ string [ ] leftParts = parts [ 0 ] . Split ( ':' ) ;
86+ string [ ] rightParts = parts . Length > 1 ? parts [ 1 ] . Split ( ':' ) : new string [ 0 ] ;
87+
88+ if ( string . IsNullOrWhiteSpace ( leftParts [ 0 ] ) )
89+ leftParts = leftParts . Skip ( 1 ) . ToArray ( ) ;
90+ if ( rightParts . Length != 0 && string . IsNullOrWhiteSpace ( rightParts . Last ( ) ) )
91+ rightParts = rightParts . Take ( rightParts . Length - 1 ) . ToArray ( ) ;
92+
93+ int numZeroesToInsert = 8 - ( leftParts . Length + rightParts . Length ) ;
94+
95+ string [ ] expandedAddress = new string [ 8 ] ;
96+ Array . Copy ( leftParts , expandedAddress , leftParts . Length ) ;
97+ for ( int i = leftParts . Length ; i < leftParts . Length + numZeroesToInsert ; i ++ )
98+ {
99+ expandedAddress [ i ] = "0000" ;
100+ }
101+ Array . Copy ( rightParts , 0 , expandedAddress , leftParts . Length + numZeroesToInsert , rightParts . Length ) ;
102+ for ( int i = 0 ; i < expandedAddress . Length ; i ++ )
103+ {
104+ switch ( expandedAddress [ i ] . Length )
105+ {
106+ case 1 :
107+ expandedAddress [ i ] = "000" + expandedAddress [ i ] ;
108+ break ;
109+ case 2 :
110+ expandedAddress [ i ] = "00" + expandedAddress [ i ] ;
111+ break ;
112+ case 3 :
113+ expandedAddress [ i ] = "0" + expandedAddress [ i ] ;
114+ break ;
115+ default :
116+ break ;
117+ }
118+ }
119+
120+ return string . Join ( ":" , expandedAddress ) ;
121+ }
122+
123+
124+ public static implicit operator IPAddress ( IPv6Address address )
125+ {
126+ byte [ ] bytes = new byte [ 16 ] ;
127+ var bigBytes = address . Raw . ToByteArray ( ) ;
128+ Array . Copy ( bigBytes , bytes , bigBytes . Length ) ;
129+ return new IPAddress ( bytes ) ;
130+ }
131+ public static implicit operator IPv6Address ( IPAddress ip )
132+ {
133+ if ( ip . AddressFamily == System . Net . Sockets . AddressFamily . InterNetworkV6 )
134+ return new IPv6Address ( ip . GetAddressBytes ( ) ) ;
135+ throw new ArgumentException ( $ "{ ip } is not a Valid IPv4 and cant be converted") ;
136+ }
137+ public static implicit operator BigInteger ( IPv6Address address )
138+ {
139+ return address . Raw ;
140+ }
141+ public static implicit operator IPv6Address ( BigInteger bigInteger )
142+ {
143+ return new IPv6Address ( bigInteger ) ;
144+ }
145+ public static implicit operator byte [ ] ( IPv6Address address )
146+ {
147+ byte [ ] bytes = new byte [ 16 ] ;
148+ var bigBytes = address . Raw . ToByteArray ( ) ;
149+ Array . Copy ( bigBytes , bytes , bigBytes . Length ) ;
150+ return bytes ;
151+ }
152+ public static implicit operator IPv6Address ( byte [ ] bytes )
153+ {
154+ return new IPv6Address ( bytes ) ;
155+ }
156+ public override string ToString ( )
157+ {
158+ return String ;
159+ }
160+
161+ public static bool operator == ( IPv6Address a , IPv6Address b )
162+ {
163+ return a . Equals ( b ) ;
164+ }
165+
166+ public static bool operator != ( IPv6Address a , IPv6Address b )
167+ {
168+ return ! a . Equals ( b ) ;
169+ }
170+
171+ public bool Equals ( IPv6Address other )
172+ {
173+ if ( this . Raw != other . Raw )
174+ return false ;
175+
176+ return true ;
177+ }
178+
179+ public override bool Equals ( object obj )
180+ {
181+ return obj is IPv6Address other &&
182+ Raw == other . Raw ;
183+ }
184+
185+ public override int GetHashCode ( )
186+ {
187+ int hashCode = 1916557166 ;
188+ hashCode = hashCode * - 1521134295 + Raw . GetHashCode ( ) ;
189+ return hashCode ;
190+ }
191+ }
192+ }
0 commit comments