2 * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.model.ietf.util;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import com.google.common.net.InetAddresses;
15 import java.net.Inet4Address;
16 import java.net.Inet6Address;
17 import java.net.InetAddress;
18 import java.net.UnknownHostException;
19 import java.util.AbstractMap.SimpleImmutableEntry;
20 import java.util.Map.Entry;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
26 * A set of utility methods to efficiently instantiate various ietf-inet-types DTOs.
29 public abstract class AbstractIetfInetUtil<A4, A4NZ extends A4, P4, A6, A6NZ extends A6, P6, A, ANZ, P> {
30 private static final int INET4_LENGTH = 4;
31 private static final int INET6_LENGTH = 16;
32 private final StringValueObjectFactory<A4> address4Factory;
33 private final StringValueObjectFactory<A4NZ> address4NoZoneFactory;
34 private final StringValueObjectFactory<P4> prefix4Factory;
35 private final StringValueObjectFactory<A6> address6Factory;
36 private final StringValueObjectFactory<A6NZ> address6NoZoneFactory;
37 private final StringValueObjectFactory<P6> prefix6Factory;
39 protected AbstractIetfInetUtil(final Class<A4> addr4Class, final Class<A4NZ> addr4nzClass,
40 final Class<P4> prefix4Class, final Class<A6> addr6Class, final Class<A6NZ> addr6nzClass,
41 final Class<P6> prefix6Class) {
42 this.address4Factory = StringValueObjectFactory.create(addr4Class, "0.0.0.0");
43 this.address4NoZoneFactory = StringValueObjectFactory.create(addr4nzClass, "0.0.0.0");
44 this.prefix4Factory = StringValueObjectFactory.create(prefix4Class, "0.0.0.0/0");
45 this.address6Factory = StringValueObjectFactory.create(addr6Class, "::0");
46 this.address6NoZoneFactory = StringValueObjectFactory.create(addr6nzClass, "::0");
47 this.prefix6Factory = StringValueObjectFactory.create(prefix6Class, "::0/0");
50 protected abstract @NonNull A ipv4Address(@NonNull A4 addr);
51 protected abstract @NonNull ANZ ipv4AddressNoZone(@NonNull A4NZ addr);
52 protected abstract @NonNull A ipv6Address(@NonNull A6 addr);
53 protected abstract @NonNull ANZ ipv6AddressNoZone(@NonNull A6NZ addr);
55 protected abstract @Nullable A4 maybeIpv4Address(@NonNull A addr);
56 protected abstract @Nullable A4NZ maybeIpv4AddressNoZone(@NonNull ANZ addr);
57 protected abstract @Nullable A6 maybeIpv6Address(@NonNull A addr);
58 protected abstract @Nullable A6NZ maybeIpv6AddressNoZone(@NonNull ANZ addr);
60 protected abstract @NonNull P ipv4Prefix(@NonNull P4 addr);
61 protected abstract @NonNull P ipv6Prefix(@NonNull P6 addr);
62 protected abstract @NonNull String ipv4AddressString(@NonNull A4 addr);
63 protected abstract @NonNull String ipv6AddressString(@NonNull A6 addr);
64 protected abstract @NonNull String ipv4PrefixString(@NonNull P4 prefix);
65 protected abstract @NonNull String ipv6PrefixString(@NonNull P6 prefix);
68 * Create an IpAddress by interpreting input bytes as an IPv4 or IPv6 address, based on array length.
70 * @param bytes 4-byte (IPv4) or 6-byte (IPv6) array
71 * @return An IpAddress object
72 * @throws IllegalArgumentException if bytes has length different from 4 or 6
73 * @throws NullPointerException if bytes is null
75 public final @NonNull A ipAddressFor(final byte @NonNull[] bytes) {
76 switch (bytes.length) {
78 return ipv4Address(ipv4AddressFor(bytes));
80 return ipv6Address(ipv6AddressFor(bytes));
82 throw new IllegalArgumentException("Invalid array length " + bytes.length);
86 public final @NonNull A ipAddressFor(final @NonNull InetAddress addr) {
87 requireNonNull(addr, "Address must not be null");
88 if (addr instanceof Inet4Address) {
89 return ipv4Address(ipv4AddressFor(addr));
90 } else if (addr instanceof Inet6Address) {
91 return ipv6Address(ipv6AddressFor(addr));
93 throw new IllegalArgumentException("Unhandled address " + addr);
98 * Create an IpAddress by interpreting input bytes as an IPv4 or IPv6 address, based on array length.
100 * @param bytes 4-byte (IPv4) or 6-byte (IPv6) array
101 * @return A no-zone IpAddress object
102 * @throws IllegalArgumentException if bytes has length different from 4 or 6
103 * @throws NullPointerException if bytes is null
105 public final @NonNull ANZ ipAddressNoZoneFor(final byte @NonNull[] bytes) {
106 switch (bytes.length) {
108 return ipv4AddressNoZone(ipv4AddressNoZoneFor(bytes));
110 return ipv6AddressNoZone(ipv6AddressNoZoneFor(bytes));
112 throw new IllegalArgumentException("Invalid array length " + bytes.length);
116 public final @NonNull ANZ ipAddressNoZoneFor(final @NonNull InetAddress addr) {
117 requireNonNull(addr, "Address must not be null");
118 if (addr instanceof Inet4Address) {
119 return ipv4AddressNoZone(ipv4AddressNoZoneFor(addr));
120 } else if (addr instanceof Inet6Address) {
121 return ipv6AddressNoZone(ipv6AddressNoZoneFor(addr));
123 throw new IllegalArgumentException("Unhandled address " + addr);
128 * Create an IpPrefix by combining the address with a mask. The address
129 * bytes are interpreted as an address and the specified mask is concatenated to
130 * it. The address bytes are not masked.
132 * @param bytes Input address as a 4-byte (IPv4) or 16-byte (IPv6) array
133 * @param mask Prefix mask
134 * @return An IpPrefix object
135 * @throws IllegalArgumentException if bytes has length different from 4 or 16 or if mask is not
136 * in range 0-32 or 0-128 respectively
137 * @throws NullPointerException if bytes is null
139 public final @NonNull P ipPrefixFor(final byte @NonNull[] bytes, final int mask) {
140 switch (bytes.length) {
142 return ipv4Prefix(ipv4PrefixFor(bytes, mask));
144 return ipv6Prefix(ipv6PrefixFor(bytes, mask));
146 throw new IllegalArgumentException("Invalid array length " + bytes.length);
150 public final @NonNull P ipPrefixFor(final @NonNull InetAddress addr, final int mask) {
151 requireNonNull(addr, "Address must not be null");
152 if (addr instanceof Inet4Address) {
153 return ipv4Prefix(ipv4PrefixFor(addr, mask));
154 } else if (addr instanceof Inet6Address) {
155 return ipv6Prefix(ipv6PrefixFor(addr, mask));
157 throw new IllegalArgumentException("Unhandled address " + addr);
161 public final @NonNull InetAddress inetAddressFor(final @NonNull A addr) {
162 final A4 v4 = maybeIpv4Address(addr);
164 return inet4AddressFor(v4);
166 final A6 v6 = maybeIpv6Address(addr);
167 checkArgument(v6 != null, "Address %s is neither IPv4 nor IPv6", addr);
168 return inet6AddressFor(v6);
171 public final @NonNull InetAddress inetAddressForNoZone(final @NonNull ANZ addr) {
172 final A4NZ v4 = maybeIpv4AddressNoZone(addr);
174 return inet4AddressForNoZone(v4);
176 final A6NZ v6 = maybeIpv6AddressNoZone(addr);
177 checkArgument(v6 != null, "Address %s is neither IPv4 nor IPv6", addr);
178 return inet6AddressForNoZone(v6);
182 public final @NonNull Inet4Address inet4AddressFor(final @NonNull A4 addr) {
184 return (Inet4Address) InetAddress.getByAddress(ipv4AddressBytes(addr));
185 } catch (UnknownHostException e) {
186 throw new IllegalArgumentException("Invalid address " + addr, e);
190 public final @NonNull Inet4Address inet4AddressForNoZone(final @NonNull A4NZ addr) {
192 return (Inet4Address) InetAddress.getByAddress(ipv4AddressNoZoneBytes(addr));
193 } catch (UnknownHostException e) {
194 throw new IllegalArgumentException("Invalid address " + addr, e);
198 public final @NonNull Inet6Address inet6AddressFor(final @NonNull A6 addr) {
200 return (Inet6Address) InetAddress.getByAddress(ipv6AddressBytes(addr));
201 } catch (UnknownHostException e) {
202 throw new IllegalArgumentException("Invalid address " + addr, e);
206 public final @NonNull Inet6Address inet6AddressForNoZone(final @NonNull A6NZ addr) {
208 return (Inet6Address) InetAddress.getByAddress(ipv6AddressNoZoneBytes(addr));
209 } catch (UnknownHostException e) {
210 throw new IllegalArgumentException("Invalid address " + addr, e);
215 * Create an Ipv4Address by interpreting input bytes as an IPv4 address.
217 * @param bytes 4-byte array
218 * @return An Ipv4Address object
219 * @throws IllegalArgumentException if bytes has length different from 4
220 * @throws NullPointerException if bytes is null
222 public final @NonNull A4 ipv4AddressFor(final byte @NonNull[] bytes) {
223 return address4Factory.newInstance(addressStringV4(bytes));
227 * Create an Ipv4Address by interpreting an {@link Inet4Address}.
229 * @param addr An {@link Inet4Address}
230 * @return An Ipv4Address object
231 * @throws IllegalArgumentException if addr is not an {@link Inet4Address}
232 * @throws NullPointerException if addr is null
234 public final @NonNull A4 ipv4AddressFor(final @NonNull InetAddress addr) {
235 requireNonNull(addr, "Address must not be null");
236 checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
237 return address4Factory.newInstance(addr.getHostAddress());
241 * Create an Ipv4AddressNoZone by interpreting input bytes as an IPv4 address.
243 * @param bytes 4-byte array
244 * @return An Ipv4AddressNoZone object
245 * @throws IllegalArgumentException if bytes has length different from 4
246 * @throws NullPointerException if bytes is null
248 public final @NonNull A4NZ ipv4AddressNoZoneFor(final byte @NonNull[] bytes) {
249 return address4NoZoneFactory.newInstance(addressStringV4(bytes));
253 * Create an Ipv4AddressNoZone by interpreting an {@link Inet4Address}.
255 * @param addr An {@link Inet4Address}
256 * @return An Ipv4AddressNoZone object
257 * @throws IllegalArgumentException if addr is not an {@link Inet4Address}
258 * @throws NullPointerException if addr is null
260 public final @NonNull A4NZ ipv4AddressNoZoneFor(final @NonNull InetAddress addr) {
261 requireNonNull(addr, "Address must not be null");
262 checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
263 return address4NoZoneFactory.newInstance(addr.getHostAddress());
266 public final @NonNull A4 ipv4AddressFrom(final @NonNull P4 prefix) {
267 return prefixToAddress(address4Factory, ipv4PrefixString(prefix));
270 public final @NonNull A4NZ ipv4AddressNoZoneFrom(final @NonNull P4 prefix) {
271 return prefixToAddress(address4NoZoneFactory, ipv4PrefixString(prefix));
274 public final byte @NonNull[] ipv4AddressBytes(final @NonNull A4 addr) {
276 * This implementation relies heavily on the input string having been validated to comply with
277 * the Ipv4Address pattern, which may include a zone index.
279 final String str = ipv4AddressString(addr);
280 final int percent = str.indexOf('%');
281 return ipv4StringBytes(str, percent == -1 ? str.length() : percent);
284 public final byte @NonNull[] ipv4AddressNoZoneBytes(final @NonNull A4NZ addr) {
286 * This implementation relies heavily on the input string having been validated to comply with
287 * the Ipv4AddressNoZone pattern, which must not include a zone index.
289 final String str = ipv4AddressString(addr);
290 return ipv4StringBytes(str, str.length());
293 private static byte @NonNull[] ipv4StringBytes(final String str, final int limit) {
294 final byte[] bytes = new byte[INET4_LENGTH];
295 Ipv4Utils.fillIpv4Bytes(bytes, 0, str, 0, limit);
300 * Create a /32 Ipv4Prefix by interpreting input bytes as an IPv4 address.
302 * @param bytes four-byte array
303 * @return An Ipv4Prefix object
304 * @throws IllegalArgumentException if bytes has length different from 4
305 * @throws NullPointerException if bytes is null
307 public final @NonNull P4 ipv4PrefixFor(final byte @NonNull[] bytes) {
308 return prefix4Factory.newInstance(prefixStringV4(bytes));
312 * Create a Ipv4Prefix by combining the address with a mask. The address
313 * bytes are interpreted as an address and the specified mask is concatenated to
314 * it. The address bytes are not masked, hence input <code>address = { 1, 2, 3, 4 }</code>
315 * and <code>mask=24</code> will result in <code>1.2.3.4/24</code>.
317 * @param address Input address as a 4-byte array
318 * @param mask Prefix mask
319 * @return An Ipv4Prefix object
320 * @throws IllegalArgumentException if bytes has length different from 4 or if mask is not in range 0-32
321 * @throws NullPointerException if bytes is null
323 public final @NonNull P4 ipv4PrefixFor(final byte @NonNull[] address, final int mask) {
324 return prefix4Factory.newInstance(prefixStringV4(address, mask));
327 public final @NonNull P4 ipv4PrefixForShort(final byte @NonNull[] address, final int mask) {
329 // Easy case, reuse the template
330 return prefix4Factory.getTemplate();
333 return v4PrefixForShort(address, 0, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
336 public final @NonNull P4 ipv4PrefixForShort(final byte @NonNull[] array, final int startOffset, final int mask) {
338 // Easy case, reuse the template
339 return prefix4Factory.getTemplate();
342 return v4PrefixForShort(array, startOffset, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
346 * Create a /32 Ipv4Prefix for an {@link Inet4Address}
348 * @param addr An {@link Inet4Address}
349 * @return An Ipv4Prefix object
350 * @throws IllegalArgumentException if addr is not an Inet4Address
351 * @throws NullPointerException if addr is null
353 public final @NonNull P4 ipv4PrefixFor(final @NonNull InetAddress addr) {
354 requireNonNull(addr, "Address must not be null");
355 checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
356 return prefix4Factory.newInstance(addr.getHostAddress() + "/32");
360 * Create a Ipv4Prefix by combining the address with a mask. The address bytes are not masked.
362 * @param addr An {@link Inet4Address}
363 * @param mask Prefix mask
364 * @return An Ipv4Prefix object
365 * @throws IllegalArgumentException if addr is not an Inet4Address or if mask is not in range 0-32
366 * @throws NullPointerException if addr is null
368 public final @NonNull P4 ipv4PrefixFor(final @NonNull InetAddress addr, final int mask) {
369 requireNonNull(addr, "Address must not be null");
370 checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
371 return newIpv4Prefix(addr.getHostAddress(), mask);
374 public final @NonNull P4 ipv4PrefixFor(final @NonNull A4 addr) {
375 requireNonNull(addr, "Address must not be null");
376 return prefix4Factory.newInstance(stripZone(ipv4AddressString(addr)) + "/32");
379 public final @NonNull P4 ipv4PrefixFor(final @NonNull A4 addr, final int mask) {
380 requireNonNull(addr, "Address must not be null");
381 return newIpv4Prefix(stripZone(ipv4AddressString(addr)), mask);
384 public final @NonNull P4 ipv4PrefixForNoZone(final @NonNull A4NZ addr) {
385 requireNonNull(addr, "Address must not be null");
386 return prefix4Factory.newInstance(ipv4AddressString(addr) + "/32");
389 public final @NonNull P4 ipv4PrefixForNoZone(final @NonNull A4NZ addr, final int mask) {
390 requireNonNull(addr, "Address must not be null");
391 return newIpv4Prefix(ipv4AddressString(addr), mask);
394 private static String stripZone(final String str) {
395 final int percent = str.indexOf('%');
396 return percent == -1 ? str : str.substring(0, percent);
399 private @NonNull P4 newIpv4Prefix(final String addr, final int mask) {
400 checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
401 return prefix4Factory.newInstance(addr + '/' + mask);
404 public final @NonNull Entry<A4, Integer> splitIpv4Prefix(final @NonNull P4 prefix) {
405 return splitPrefix(address4Factory, ipv4PrefixString(prefix));
408 public final @NonNull Entry<A4NZ, Integer> splitIpv4PrefixNoZone(final @NonNull P4 prefix) {
409 return splitPrefix(address4NoZoneFactory, ipv4PrefixString(prefix));
412 public final byte @NonNull[] ipv4PrefixToBytes(final @NonNull P4 prefix) {
413 final String str = ipv4PrefixString(prefix);
414 final int slash = str.lastIndexOf('/');
416 final byte[] bytes = new byte[INET4_LENGTH + 1];
417 Ipv4Utils.fillIpv4Bytes(bytes, 0, str, 0, slash);
418 bytes[INET4_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
423 * Create an Ipv6Address by interpreting input bytes as an IPv6 address.
425 * @param bytes 16-byte array
426 * @return An Ipv6Address object
427 * @throws IllegalArgumentException if bytes has length different from 16
428 * @throws NullPointerException if bytes is null
430 public final @NonNull A6 ipv6AddressFor(final byte @NonNull[] bytes) {
431 return address6Factory.newInstance(addressStringV6(bytes));
435 * Create an Ipv6Address by interpreting an {@link Inet6Address}.
437 * @param addr An {@link Inet6Address}
438 * @return An Ipv6Address object
439 * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
440 * @throws NullPointerException if addr is null
442 public final @NonNull A6 ipv6AddressFor(final @NonNull InetAddress addr) {
443 requireNonNull(addr, "Address must not be null");
444 checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
445 return address6Factory.newInstance(addressStringV6(addr));
449 * Create an Ipv6AddressNoZone by interpreting input bytes as an IPv6 address.
451 * @param bytes 16-byte array
452 * @return An Ipv6Address object
453 * @throws IllegalArgumentException if bytes has length different from 16
454 * @throws NullPointerException if bytes is null
456 public final @NonNull A6NZ ipv6AddressNoZoneFor(final byte @NonNull[] bytes) {
457 return address6NoZoneFactory.newInstance(addressStringV6(bytes));
461 * Create an Ipv6AddressNoZone by interpreting an {@link Inet6Address}.
463 * @param addr An {@link Inet6Address}
464 * @return An Ipv6AddressNoZone object
465 * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
466 * @throws NullPointerException if addr is null
468 public final @NonNull A6NZ ipv6AddressNoZoneFor(final @NonNull InetAddress addr) {
469 requireNonNull(addr, "Address must not be null");
470 checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
471 return address6NoZoneFactory.newInstance(addressStringV6(addr));
474 public final @NonNull A6 ipv6AddressFrom(final @NonNull P6 prefix) {
475 return prefixToAddress(address6Factory, ipv6PrefixString(prefix));
478 public final @NonNull A6NZ ipv6AddressNoZoneFrom(final @NonNull P6 prefix) {
479 return prefixToAddress(address6NoZoneFactory, ipv6PrefixString(prefix));
482 public final byte @NonNull[] ipv6AddressBytes(final @NonNull A6 addr) {
483 final String str = ipv6AddressString(addr);
484 final int percent = str.indexOf('%');
485 return ipv6StringBytes(str, percent == -1 ? str.length() : percent);
488 public final byte @NonNull[] ipv6AddressNoZoneBytes(final @NonNull A6NZ addr) {
489 final String str = ipv6AddressString(addr);
490 return ipv6StringBytes(str, str.length());
493 private static byte @NonNull[] ipv6StringBytes(final @NonNull String str, final int limit) {
494 final byte[] bytes = new byte[INET6_LENGTH];
495 Ipv6Utils.fillIpv6Bytes(bytes, str, limit);
500 * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
502 * @param bytes four-byte array
503 * @return An Ipv6Prefix object
504 * @throws IllegalArgumentException if bytes has length different from 16
505 * @throws NullPointerException if bytes is null
507 public final @NonNull P6 ipv6PrefixFor(final byte @NonNull[] bytes) {
508 return prefix6Factory.newInstance(addressStringV6(bytes) + "/128");
512 * Create a Ipv6Prefix by combining the address with a mask. The address
513 * bytes are interpreted as an address and the specified mask is concatenated to
514 * it. The address bytes are not masked.
516 * @param address Input address as a 16-byte array
517 * @param mask Prefix mask
518 * @return An Ipv6Prefix object
519 * @throws IllegalArgumentException if bytes has length different from 16 or if mask is not in range 0-128
520 * @throws NullPointerException if bytes is null
522 public final @NonNull P6 ipv6PrefixFor(final byte @NonNull[] address, final int mask) {
523 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
524 return prefix6Factory.newInstance(addressStringV6(address) + '/' + mask);
527 public final @NonNull P6 ipv6PrefixForShort(final byte @NonNull[] address, final int mask) {
528 return ipv6PrefixForShort(address, 0, mask);
531 public final @NonNull P6 ipv6PrefixForShort(final byte @NonNull[] array, final int startOffset, final int mask) {
533 // Easy case, reuse the template
534 return prefix6Factory.getTemplate();
537 checkArgument(mask > 0 && mask <= 128, "Invalid mask %s", mask);
538 final int size = mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1);
540 // Until we can instantiate an IPv6 address for a partial array, use a temporary buffer
541 byte[] tmp = new byte[INET6_LENGTH];
542 System.arraycopy(array, startOffset, tmp, 0, size);
543 return ipv6PrefixFor(tmp, mask);
547 * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv4 address.
549 * @param addr an {@link Inet6Address}
550 * @return An Ipv6Prefix object
551 * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
552 * @throws NullPointerException if addr is null
554 public final @NonNull P6 ipv6PrefixFor(final @NonNull InetAddress addr) {
555 return prefix6Factory.newInstance(addressStringV6(addr) + "/128");
559 * Create a Ipv6Prefix by combining the address with a mask. The address
560 * bytes are interpreted as an address and the specified mask is concatenated to
561 * it. The address bytes are not masked.
563 * @param addr Input address
564 * @param mask Prefix mask
565 * @return An Ipv6Prefix object
566 * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
567 * @throws NullPointerException if addr is null
569 public final @NonNull P6 ipv6PrefixFor(final @NonNull InetAddress addr, final int mask) {
570 requireNonNull(addr, "Address must not be null");
571 checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
572 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
573 return prefix6Factory.newInstance(addressStringV6(addr) + '/' + mask);
576 public final @NonNull P6 ipv6PrefixFor(final @NonNull A6 addr) {
577 requireNonNull(addr, "Address must not be null");
578 return prefix6Factory.newInstance(stripZone(ipv6AddressString(addr)) + "/128");
581 public final @NonNull P6 ipv6PrefixFor(final @NonNull A6 addr, final int mask) {
582 requireNonNull(addr, "Address must not be null");
583 return newIpv6Prefix(stripZone(ipv6AddressString(addr)), mask);
586 public final @NonNull P6 ipv6PrefixForNoZone(final @NonNull A6NZ addr) {
587 requireNonNull(addr, "Address must not be null");
588 return prefix6Factory.newInstance(ipv6AddressString(addr) + "/128");
591 public final @NonNull P6 ipv6PrefixForNoZone(final @NonNull A6NZ addr, final int mask) {
592 requireNonNull(addr, "Address must not be null");
593 return newIpv6Prefix(ipv6AddressString(addr), mask);
596 private P6 newIpv6Prefix(final String addr, final int mask) {
597 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
598 return prefix6Factory.newInstance(addr + '/' + mask);
601 public final @NonNull Entry<A6, Integer> splitIpv6Prefix(final @NonNull P6 prefix) {
602 return splitPrefix(address6Factory, ipv6PrefixString(prefix));
605 public final @NonNull Entry<A6NZ, Integer> splitIpv6PrefixNoZone(final @NonNull P6 prefix) {
606 return splitPrefix(address6NoZoneFactory, ipv6PrefixString(prefix));
609 private static <T> @NonNull T prefixToAddress(final StringValueObjectFactory<T> factory, final String str) {
610 return factory.newInstance(str.substring(0, str.lastIndexOf('/')));
613 private static <T> @NonNull Entry<T, Integer> splitPrefix(final StringValueObjectFactory<T> factory,
615 final int slash = str.lastIndexOf('/');
616 return new SimpleImmutableEntry<>(factory.newInstance(str.substring(0, slash)),
617 Integer.valueOf(str.substring(slash + 1)));
620 public final byte @NonNull[] ipv6PrefixToBytes(final @NonNull P6 prefix) {
621 final String str = ipv6PrefixString(prefix);
622 final byte[] bytes = new byte[INET6_LENGTH + 1];
623 final int slash = str.lastIndexOf('/');
624 Ipv6Utils.fillIpv6Bytes(bytes, str, slash);
625 bytes[INET6_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
629 private static void appendIpv4String(final StringBuilder sb, final byte @NonNull[] bytes) {
630 checkArgument(bytes.length == INET4_LENGTH, "IPv4 address length is 4 bytes");
632 sb.append(Byte.toUnsignedInt(bytes[0]));
633 for (int i = 1; i < INET4_LENGTH; ++i) {
634 sb.append('.').append(Byte.toUnsignedInt(bytes[i]));
638 private static String addressStringV4(final byte @NonNull[] bytes) {
639 final StringBuilder sb = new StringBuilder(15);
640 appendIpv4String(sb, bytes);
641 return sb.toString();
644 private static String addressStringV6(final byte @NonNull[] bytes) {
645 checkArgument(bytes.length == INET6_LENGTH, "IPv6 address length is 16 bytes");
648 return addressStringV6(Inet6Address.getByAddress(bytes));
649 } catch (UnknownHostException e) {
650 throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
654 private static String addressStringV6(final InetAddress addr) {
655 return InetAddresses.toAddrString(addr);
658 private static String prefixStringV4(final byte @NonNull[] bytes) {
659 final StringBuilder sb = new StringBuilder(18);
660 appendIpv4String(sb, bytes);
661 return sb.append("/32").toString();
664 private static String prefixStringV4(final byte @NonNull[] bytes, final int mask) {
665 checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
667 final StringBuilder sb = new StringBuilder(18);
668 appendIpv4String(sb, bytes);
669 return sb.append('/').append(mask).toString();
672 private P4 v4PrefixForShort(final byte @NonNull[] array, final int startOffset, final int size, final int mask) {
673 if (startOffset == 0 && size == INET4_LENGTH && array.length == INET4_LENGTH) {
674 // Easy case, fall back to non-short
675 return ipv4PrefixFor(array, mask);
678 final StringBuilder sb = new StringBuilder(18);
681 sb.append(Byte.toUnsignedInt(array[startOffset]));
682 for (int i = 1; i < size; i++) {
683 sb.append('.').append(Byte.toUnsignedInt(array[startOffset + i]));
687 for (int i = size; i < INET4_LENGTH; i++) {
692 checkArgument(mask > 0 && mask <= 32, "Invalid mask %s", mask);
693 sb.append('/').append(mask);
695 return prefix4Factory.newInstance(sb.toString());