blob: dcbd4a1907aa419472b889d95cbbd9c089d23e2c [file] [log] [blame]
Yotam Harcholf3f11152013-09-05 16:47:16 -07001package org.projectfloodlight.openflow.types;
2
Ronald Lifea17892014-07-03 18:36:06 -07003import java.math.BigInteger;
Ronald Licc9c9302014-07-07 00:58:56 -07004import java.net.Inet6Address;
5import java.net.InetAddress;
Andreas Wundsam5f71b412014-02-18 12:56:35 -08006import java.util.Arrays;
Yotam Harcholf3f11152013-09-05 16:47:16 -07007import java.util.regex.Pattern;
8
Andreas Wundsam3700d162014-03-11 04:43:38 -07009import javax.annotation.Nonnull;
10
Yotam Harcholf3f11152013-09-05 16:47:16 -070011import org.jboss.netty.buffer.ChannelBuffer;
12import org.projectfloodlight.openflow.exceptions.OFParseError;
Andreas Wundsam5f71b412014-02-18 12:56:35 -080013
Sovietaced9dfc1ef2014-06-27 11:13:57 -070014import com.google.common.base.Preconditions;
Andreas Wundsam22ba3af2013-10-04 16:00:30 -070015import com.google.common.hash.PrimitiveSink;
Andreas Wundsam85c961f2013-09-29 21:22:12 -070016import com.google.common.primitives.Longs;
17
Yotam Harcholf3f11152013-09-05 16:47:16 -070018/**
19 * IPv6 address object. Instance controlled, immutable. Internal representation:
20 * two 64 bit longs (not that you'd have to know).
21 *
22 * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
23 */
Yotam Harchol4d634682013-09-26 13:21:06 -070024public class IPv6Address extends IPAddress<IPv6Address> {
Yotam Harcholf3f11152013-09-05 16:47:16 -070025 static final int LENGTH = 16;
26 private final long raw1;
27 private final long raw2;
28
Gregor Maier1acb4502013-12-12 11:25:07 -080029 private static final int NOT_A_CIDR_MASK = -1;
30 private static final int CIDR_MASK_CACHE_UNSET = -2;
Gregor Maier5615b6c2013-12-11 22:29:07 -080031 // Must appear before the static IPv4Address constant assignments
32 private volatile int cidrMaskLengthCache = CIDR_MASK_CACHE_UNSET;
33
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -070034 private final static long NONE_VAL1 = 0x0L;
35 private final static long NONE_VAL2 = 0x0L;
36 public static final IPv6Address NONE = new IPv6Address(NONE_VAL1, NONE_VAL2);
37
Gregor Maier5615b6c2013-12-11 22:29:07 -080038
Yotam Harchola289d552013-09-16 10:10:40 -070039 public static final IPv6Address NO_MASK = IPv6Address.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl);
40 public static final IPv6Address FULL_MASK = IPv6Address.of(0x0, 0x0);
Yotam Harcholf3f11152013-09-05 16:47:16 -070041
Yotam Harchola289d552013-09-16 10:10:40 -070042 private IPv6Address(final long raw1, final long raw2) {
Yotam Harcholf3f11152013-09-05 16:47:16 -070043 this.raw1 = raw1;
44 this.raw2 = raw2;
45 }
46
Yotam Harchol4d634682013-09-26 13:21:06 -070047 @Override
Yotam Harcholeb023dc2013-09-26 15:45:44 -070048 public IPVersion getIpVersion() {
49 return IPVersion.IPv6;
Yotam Harchol4d634682013-09-26 13:21:06 -070050 }
51
Gregor Maier7f987e62013-12-10 19:34:18 -080052
Gregor Maier5615b6c2013-12-11 22:29:07 -080053 private int computeCidrMask64(long raw) {
54 long mask = raw;
55 if (raw == 0)
56 return 0;
Gregor Maier1acb4502013-12-12 11:25:07 -080057 else if (Long.bitCount((~mask) + 1) == 1) {
Gregor Maier5615b6c2013-12-11 22:29:07 -080058 // represent a true CIDR prefix length
59 return Long.bitCount(mask);
60 }
61 else {
62 // Not a true prefix
63 return NOT_A_CIDR_MASK;
64 }
65 }
66
67 private int asCidrMaskLengthInternal() {
68 if (cidrMaskLengthCache == CIDR_MASK_CACHE_UNSET) {
Gregor Maier1acb4502013-12-12 11:25:07 -080069 // No synchronization needed. Writing cidrMaskLengthCache only once
70 if (raw1 == 0 && raw2 == 0) {
71 cidrMaskLengthCache = 0;
72 } else if (raw1 == -1L) {
73 // top half is all 1 bits
74 int tmpLength = computeCidrMask64(raw2);
75 if (tmpLength != NOT_A_CIDR_MASK)
76 tmpLength += 64;
77 cidrMaskLengthCache = tmpLength;
78 } else if (raw2 == 0) {
79 cidrMaskLengthCache = computeCidrMask64(raw1);
80 } else {
81 cidrMaskLengthCache = NOT_A_CIDR_MASK;
Gregor Maier5615b6c2013-12-11 22:29:07 -080082 }
83 }
84 return cidrMaskLengthCache;
85 }
86
87 @Override
88 public boolean isCidrMask() {
89 return asCidrMaskLengthInternal() != NOT_A_CIDR_MASK;
90 }
91
Gregor Maier7f987e62013-12-10 19:34:18 -080092 @Override
93 public int asCidrMaskLength() {
Gregor Maier5615b6c2013-12-11 22:29:07 -080094 if (!isCidrMask()) {
95 throw new IllegalStateException("IP is not a valid CIDR prefix " +
96 "mask " + toString());
Gregor Maier7f987e62013-12-10 19:34:18 -080097 } else {
Gregor Maier5615b6c2013-12-11 22:29:07 -080098 return asCidrMaskLengthInternal();
Gregor Maier7f987e62013-12-10 19:34:18 -080099 }
100 }
101
Aditya Vaja56b8b182014-03-11 13:13:58 -0700102 @Override
103 public boolean isBroadcast() {
104 return this.equals(NO_MASK);
105 }
106
107 @Override
Aditya Vaja98c96e72014-03-11 15:19:01 -0700108 public IPv6Address and(IPv6Address other) {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700109 Preconditions.checkNotNull(other, "other must not be null");
110
111 IPv6Address otherIp = other;
Aditya Vaja56b8b182014-03-11 13:13:58 -0700112 return IPv6Address.of((raw1 & otherIp.raw1), (raw2 & otherIp.raw2));
113 }
114
115 @Override
Aditya Vaja98c96e72014-03-11 15:19:01 -0700116 public IPv6Address or(IPv6Address other) {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700117 Preconditions.checkNotNull(other, "other must not be null");
118
119 IPv6Address otherIp = other;
Aditya Vaja56b8b182014-03-11 13:13:58 -0700120 return IPv6Address.of((raw1 | otherIp.raw1), (raw2 | otherIp.raw2));
121 }
122
123 @Override
124 public IPv6Address not() {
125 return IPv6Address.of(~raw1, ~raw2);
126 }
127
Ronald Licc9c9302014-07-07 00:58:56 -0700128 /**
129 * Returns an {@code IPv6Address} object that represents the given
130 * IP address. The argument is in network byte order: the highest
131 * order byte of the address is in {@code address[0]}.
132 * <p>
133 * The address byte array must be 16 bytes long (128 bits long).
134 * <p>
135 * Similar to {@link InetAddress#getByAddress(byte[])}.
136 *
137 * @param address the raw IP address in network byte order
138 * @return an {@code IPv6Address} object that represents the given
139 * raw IP address
140 * @throws NullPointerException if the given address was {@code null}
141 * @throws IllegalArgumentException if the given address was of an invalid
142 * byte array length
143 * @see InetAddress#getByAddress(byte[])
144 */
145 @Nonnull
146 public static IPv6Address of(@Nonnull final byte[] address) {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700147 Preconditions.checkNotNull(address, "address must not be null");
148
Yotam Harcholf3f11152013-09-05 16:47:16 -0700149 if (address.length != LENGTH) {
150 throw new IllegalArgumentException(
Andreas Wundsamc85b5c52013-09-24 13:01:43 -0700151 "Invalid byte array length for IPv6 address: " + address.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700152 }
153
154 long raw1 =
155 (address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
156 | (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
157 | (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
158 | (address[6] & 0xFFL) << 8 | (address[7]);
159
160 long raw2 =
161 (address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
162 | (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
163 | (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
164 | (address[14] & 0xFFL) << 8 | (address[15]);
165
Yotam Harchola289d552013-09-16 10:10:40 -0700166 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700167 }
168
169 private static class IPv6Builder {
170 private long raw1, raw2;
171
172 public void setUnsignedShortWord(final int i, final int value) {
173 int shift = 48 - (i % 4) * 16;
174
175 if (value < 0 || value > 0xFFFF)
176 throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
177
178 if (i >= 0 && i < 4)
179 raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
180 else if (i >= 4 && i < 8)
181 raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
182 else
183 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
184 }
185
Yotam Harchola289d552013-09-16 10:10:40 -0700186 public IPv6Address getIPv6() {
187 return IPv6Address.of(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700188 }
189 }
190
191 private final static Pattern colonPattern = Pattern.compile(":");
192
Ronald Licc9c9302014-07-07 00:58:56 -0700193 /**
194 * Returns an {@code IPv6Address} object that represents the given
195 * IP address. The argument is in the conventional string representation
196 * of IPv6 addresses.
197 * <p>
198 * Expects up to 8 groups of 16-bit hex words seperated by colons
199 * (e.g., 2001:db8:85a3:8d3:1319:8a2e:370:7348).
200 * <p>
201 * Supports zero compression (e.g., 2001:db8::7348).
202 * Does <b>not</b> currently support embedding a dotted-quad IPv4 address
203 * into the IPv6 address (e.g., 2001:db8::192.168.0.1).
Andreas Wundsam3700d162014-03-11 04:43:38 -0700204 *
Ronald Licc9c9302014-07-07 00:58:56 -0700205 * @param string the IP address in the conventional string representation
206 * of IPv6 addresses
207 * @return an {@code IPv6Address} object that represents the given
208 * IP address
209 * @throws NullPointerException if the given string was {@code null}
210 * @throws IllegalArgumentException if the given string was not a valid
211 * IPv6 address
Andreas Wundsam3700d162014-03-11 04:43:38 -0700212 */
213 @Nonnull
214 public static IPv6Address of(@Nonnull final String string) throws IllegalArgumentException {
Sovietaced9dfc1ef2014-06-27 11:13:57 -0700215 Preconditions.checkNotNull(string, "string must not be null");
216
Yotam Harcholf3f11152013-09-05 16:47:16 -0700217 IPv6Builder builder = new IPv6Builder();
218 String[] parts = colonPattern.split(string, -1);
219
220 int leftWord = 0;
221 int leftIndex = 0;
222
223 boolean hitZeroCompression = false;
224
225 for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
226 String part = parts[leftIndex];
227 if (part.length() == 0) {
228 // hit empty group of zero compression
229 hitZeroCompression = true;
230 break;
231 }
232 builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
233 }
234
235 if (hitZeroCompression) {
236 if (leftIndex == 0) {
237 // if colon is at the start, two columns must be at the start,
238 // move to the second empty group
239 leftIndex = 1;
240 if (parts.length < 2 || parts[1].length() > 0)
241 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
242 }
243
244 int rightWord = 7;
245 int rightIndex;
246 for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
247 String part = parts[rightIndex];
248 if (part.length() == 0)
249 break;
250 builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
251 }
252 if (rightIndex == parts.length - 1) {
253 // if colon is at the end, two columns must be at the end, move
254 // to the second empty group
255 if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
256 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
257 rightIndex--;
258 }
259 if (leftIndex != rightIndex)
260 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
261 } else {
262 if (leftIndex != 8) {
263 throw new IllegalArgumentException("Malformed IPv6 address: " + string);
264 }
265 }
266 return builder.getIPv6();
267 }
268
Ronald Licc9c9302014-07-07 00:58:56 -0700269 /**
270 * Returns an {@code IPv6Address} object that represents the given
271 * IP address. The arguments are the two 64-bit integers representing
272 * the first (higher-order) and second (lower-order) 64-bit blocks
273 * of the IP address.
Andreas Wundsam3700d162014-03-11 04:43:38 -0700274 *
Ronald Licc9c9302014-07-07 00:58:56 -0700275 * @param raw1 the first (higher-order) 64-bit block of the IP address
276 * @param raw2 the second (lower-order) 64-bit block of the IP address
277 * @return an {@code IPv6Address} object that represents the given
278 * raw IP address
Andreas Wundsam3700d162014-03-11 04:43:38 -0700279 */
Ronald Licc9c9302014-07-07 00:58:56 -0700280 @Nonnull
Yotam Harchola289d552013-09-16 10:10:40 -0700281 public static IPv6Address of(final long raw1, final long raw2) {
Andreas Wundsamb75c4ad2013-09-23 14:45:35 -0700282 if(raw1==NONE_VAL1 && raw2 == NONE_VAL2)
283 return NONE;
Yotam Harchola289d552013-09-16 10:10:40 -0700284 return new IPv6Address(raw1, raw2);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700285 }
286
Ronald Licc9c9302014-07-07 00:58:56 -0700287 /**
288 * Returns an {@code IPv6Address} object that represents the given
289 * IP address. The argument is given as an {@code Inet6Address} object.
290 *
291 * @param address the IP address as an {@code Inet6Address} object
292 * @return an {@code IPv6Address} object that represents the
293 * given IP address
294 * @throws NullPointerException if the given {@code Inet6Address} was
295 * {@code null}
296 */
297 @Nonnull
298 public static IPv6Address of(@Nonnull final Inet6Address address) {
299 Preconditions.checkNotNull(address, "address must not be null");
300 return IPv6Address.of(address.getAddress());
301 }
302
303 /**
304 * Returns an {@code IPv6Address} object that represents the
305 * CIDR subnet mask of the given prefix length.
306 *
307 * @param cidrMaskLength the prefix length of the CIDR subnet mask
308 * (i.e. the number of leading one-bits),
309 * where {@code 0 <= cidrMaskLength <= 128}
310 * @return an {@code IPv6Address} object that represents the
311 * CIDR subnet mask of the given prefix length
312 * @throws IllegalArgumentException if the given prefix length was invalid
313 */
314 @Nonnull
315 public static IPv6Address ofCidrMaskLength(final int cidrMaskLength) {
Ronald Lifea17892014-07-03 18:36:06 -0700316 Preconditions.checkArgument(
317 cidrMaskLength >= 0 && cidrMaskLength <= 128,
318 "Invalid IPv6 CIDR mask length: %s", cidrMaskLength);
319
320 if (cidrMaskLength == 128) {
321 return IPv6Address.NO_MASK;
322 } else if (cidrMaskLength == 0) {
323 return IPv6Address.FULL_MASK;
324 } else {
325 BigInteger mask
326 = BigInteger.ONE.negate().shiftLeft(128 - cidrMaskLength);
327 byte[] maskBytesTemp = mask.toByteArray();
328 byte[] maskBytes;
329 if (maskBytesTemp.length < 16) {
330 maskBytes = new byte[16];
331 System.arraycopy(maskBytesTemp, 0,
332 maskBytes, 16 - maskBytesTemp.length,
333 maskBytesTemp.length);
334 Arrays.fill(maskBytes, 0, 16 - maskBytesTemp.length, (byte)(0xFF));
335 } else if (maskBytesTemp.length > 16) {
336 maskBytes = new byte[16];
337 System.arraycopy(maskBytesTemp, 0, maskBytes, 0, maskBytes.length);
338 } else {
339 maskBytes = maskBytesTemp;
340 }
341 return IPv6Address.of(maskBytes);
342 }
343 }
344
Ronald Libbf01942014-07-07 17:00:13 -0700345 /**
346 * Returns an {@code IPv6AddressWithMask} object that represents this
347 * IP address masked by the given IP address mask.
348 *
349 * @param mask the {@code IPv6Address} object that represents the mask
350 * @return an {@code IPv6AddressWithMask} object that represents this
351 * IP address masked by the given mask
352 * @throws NullPointerException if the given mask was {@code null}
353 */
354 @Nonnull
355 @Override
356 public IPv6AddressWithMask withMask(@Nonnull final IPv6Address mask) {
357 return IPv6AddressWithMask.of(this, mask);
358 }
359
360 /**
361 * Returns an {@code IPv6AddressWithMask} object that represents this
362 * IP address masked by the CIDR subnet mask of the given prefix length.
363 *
364 * @param cidrMaskLength the prefix length of the CIDR subnet mask
365 * (i.e. the number of leading one-bits),
366 * where {@code 0 <= cidrMaskLength <= 128}
367 * @return an {@code IPv6AddressWithMask} object that
368 * represents this IP address masked by the CIDR
369 * subnet mask of the given prefix length
370 * @throws IllegalArgumentException if the given prefix length was invalid
371 * @see #ofCidrMaskLength(int)
372 */
373 @Nonnull
374 @Override
375 public IPv6AddressWithMask withMaskOfLength(final int cidrMaskLength) {
376 return this.withMask(IPv6Address.ofCidrMaskLength(cidrMaskLength));
377 }
378
Andreas Wundsam4e2469e2014-02-17 15:32:43 -0800379 private volatile byte[] bytesCache = null;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700380
381 public byte[] getBytes() {
382 if (bytesCache == null) {
383 synchronized (this) {
384 if (bytesCache == null) {
385 bytesCache =
386 new byte[] { (byte) ((raw1 >> 56) & 0xFF),
387 (byte) ((raw1 >> 48) & 0xFF),
388 (byte) ((raw1 >> 40) & 0xFF),
389 (byte) ((raw1 >> 32) & 0xFF),
390 (byte) ((raw1 >> 24) & 0xFF),
391 (byte) ((raw1 >> 16) & 0xFF),
392 (byte) ((raw1 >> 8) & 0xFF),
393 (byte) ((raw1 >> 0) & 0xFF),
394
395 (byte) ((raw2 >> 56) & 0xFF),
396 (byte) ((raw2 >> 48) & 0xFF),
397 (byte) ((raw2 >> 40) & 0xFF),
398 (byte) ((raw2 >> 32) & 0xFF),
399 (byte) ((raw2 >> 24) & 0xFF),
400 (byte) ((raw2 >> 16) & 0xFF),
401 (byte) ((raw2 >> 8) & 0xFF),
402 (byte) ((raw2 >> 0) & 0xFF) };
403 }
404 }
405 }
Andreas Wundsam5f71b412014-02-18 12:56:35 -0800406 return Arrays.copyOf(bytesCache, bytesCache.length);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700407 }
408
409 @Override
410 public int getLength() {
411 return LENGTH;
412 }
413
414 @Override
415 public String toString() {
416 return toString(true, false);
417 }
418
419 public int getUnsignedShortWord(final int i) {
420 if (i >= 0 && i < 4)
421 return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
422 else if (i >= 4 && i < 8)
423 return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
424 else
425 throw new IllegalArgumentException("16 bit word index must be in [0,7]");
426 }
427
428 /** get the index of the first word where to apply IPv6 zero compression */
429 public int getZeroCompressStart() {
430 int start = Integer.MAX_VALUE;
431 int maxLength = -1;
432
433 int candidateStart = -1;
434
435 for (int i = 0; i < 8; i++) {
436 if (candidateStart >= 0) {
437 // in a zero octect
438 if (getUnsignedShortWord(i) != 0) {
439 // end of this candidate word
440 int candidateLength = i - candidateStart;
441 if (candidateLength >= maxLength) {
442 start = candidateStart;
443 maxLength = candidateLength;
444 }
445 candidateStart = -1;
446 }
447 } else {
448 // not in a zero octect
449 if (getUnsignedShortWord(i) == 0) {
450 candidateStart = i;
451 }
452 }
453 }
454
455 if (candidateStart >= 0) {
456 int candidateLength = 8 - candidateStart;
457 if (candidateLength >= maxLength) {
458 start = candidateStart;
459 maxLength = candidateLength;
460 }
461 }
462
463 return start;
464 }
465
466 public String toString(final boolean zeroCompression, final boolean leadingZeros) {
467 StringBuilder res = new StringBuilder();
468
469 int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
470 boolean inCompression = false;
471 boolean colonNeeded = false;
472
473 for (int i = 0; i < 8; i++) {
474 int word = getUnsignedShortWord(i);
475
476 if (word == 0) {
477 if (inCompression)
478 continue;
479 else if (i == compressionStart) {
480 res.append(':').append(':');
481 inCompression = true;
482 colonNeeded = false;
483 continue;
484 }
485 } else {
486 inCompression = false;
487 }
488
489 if (colonNeeded) {
490 res.append(':');
491 colonNeeded = false;
492 }
493
494 res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
495 16));
496 colonNeeded = true;
497 }
498 return res.toString();
499 }
500
501 @Override
502 public int hashCode() {
503 final int prime = 31;
504 int result = 1;
505 result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
506 result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
507 return result;
508 }
509
510 @Override
511 public boolean equals(final Object obj) {
512 if (this == obj)
513 return true;
514 if (obj == null)
515 return false;
516 if (getClass() != obj.getClass())
517 return false;
Yotam Harchola289d552013-09-16 10:10:40 -0700518 IPv6Address other = (IPv6Address) obj;
Yotam Harcholf3f11152013-09-05 16:47:16 -0700519 if (raw1 != other.raw1)
520 return false;
521 if (raw2 != other.raw2)
522 return false;
523 return true;
524 }
525
526 public void write16Bytes(ChannelBuffer c) {
527 c.writeLong(this.raw1);
528 c.writeLong(this.raw2);
529 }
530
Yotam Harchola289d552013-09-16 10:10:40 -0700531 public static IPv6Address read16Bytes(ChannelBuffer c) throws OFParseError {
532 return IPv6Address.of(c.readLong(), c.readLong());
Yotam Harcholf3f11152013-09-05 16:47:16 -0700533 }
534
535 @Override
Yotam Harchola289d552013-09-16 10:10:40 -0700536 public IPv6Address applyMask(IPv6Address mask) {
Aditya Vaja98c96e72014-03-11 15:19:01 -0700537 return and(mask);
Yotam Harcholf3f11152013-09-05 16:47:16 -0700538 }
Andreas Wundsam85c961f2013-09-29 21:22:12 -0700539
540 @Override
541 public int compareTo(IPv6Address o) {
542 int res = Longs.compare(raw1, o.raw1);
543 if(res != 0)
544 return res;
545 else
546 return Longs.compare(raw2, o.raw2);
547 }
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700548
549 @Override
550 public void putTo(PrimitiveSink sink) {
551 sink.putLong(raw1);
552 sink.putLong(raw2);
553 }
Yotam Harcholf3f11152013-09-05 16:47:16 -0700554}