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