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 javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23 import org.opendaylight.yangtools.yang.binding.util.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 @Nonnull protected abstract A ipv4Address(@Nonnull A4 addr);
51 @Nonnull protected abstract ANZ ipv4AddressNoZone(@Nonnull A4NZ addr);
52 @Nonnull protected abstract A ipv6Address(@Nonnull A6 addr);
53 @Nonnull protected abstract ANZ ipv6AddressNoZone(@Nonnull A6NZ addr);
55 @Nullable protected abstract A4 maybeIpv4Address(@Nonnull A addr);
56 @Nullable protected abstract A4NZ maybeIpv4AddressNoZone(@Nonnull ANZ addr);
57 @Nullable protected abstract A6 maybeIpv6Address(@Nonnull A addr);
58 @Nullable protected abstract A6NZ maybeIpv6AddressNoZone(@Nonnull ANZ addr);
60 @Nonnull protected abstract P ipv4Prefix(@Nonnull P4 addr);
61 @Nonnull protected abstract P ipv6Prefix(@Nonnull P6 addr);
62 @Nonnull protected abstract String ipv4AddressString(@Nonnull A4 addr);
63 @Nonnull protected abstract String ipv6AddressString(@Nonnull A6 addr);
64 @Nonnull protected abstract String ipv4PrefixString(@Nonnull P4 prefix);
65 @Nonnull protected abstract 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 @Nonnull public final A ipAddressFor(@Nonnull final byte[] 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 @Nonnull public final A ipAddressFor(@Nonnull final 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 @Nonnull public final ANZ ipAddressNoZoneFor(@Nonnull final byte[] 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 @Nonnull public final ANZ ipAddressNoZoneFor(@Nonnull final 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 @Nonnull public final P ipPrefixFor(@Nonnull final byte[] 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 @Nonnull public final P ipPrefixFor(@Nonnull final 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 @Nonnull public final InetAddress inetAddressFor(@Nonnull final 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 @Nonnull public final InetAddress inetAddressForNoZone(@Nonnull final ANZ addr) {
172 final A4NZ v4 = maybeIpv4AddressNoZone(addr);
174 return inet4AddressFor(v4);
176 final A6NZ v6 = maybeIpv6AddressNoZone(addr);
177 checkArgument(v6 != null, "Address %s is neither IPv4 nor IPv6", addr);
178 return inet6AddressFor(v6);
182 @Nonnull public final Inet4Address inet4AddressFor(@Nonnull final A4 addr) {
184 return (Inet4Address) InetAddress.getByAddress(ipv4AddressBytes(addr));
185 } catch (UnknownHostException e) {
186 throw new IllegalArgumentException("Invalid address " + addr, e);
190 @Nonnull public final Inet4Address inet4AddressForNoZone(@Nonnull final A4NZ addr) {
192 return (Inet4Address) InetAddress.getByAddress(ipv4AddressNoZoneBytes(addr));
193 } catch (UnknownHostException e) {
194 throw new IllegalArgumentException("Invalid address " + addr, e);
198 @Nonnull public final Inet6Address inet6AddressFor(@Nonnull final A6 addr) {
200 return (Inet6Address) InetAddress.getByAddress(ipv6AddressBytes(addr));
201 } catch (UnknownHostException e) {
202 throw new IllegalArgumentException("Invalid address " + addr, e);
206 @Nonnull public final Inet6Address inet6AddressForNoZone(@Nonnull final 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 @Nonnull public final A4 ipv4AddressFor(@Nonnull final byte[] 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 @Nonnull public final A4 ipv4AddressFor(@Nonnull final 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 @Nonnull public final A4NZ ipv4AddressNoZoneFor(@Nonnull final byte[] 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 @Nonnull public final A4NZ ipv4AddressNoZoneFor(@Nonnull final 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 @Nonnull public final A4 ipv4AddressFrom(@Nonnull final P4 prefix) {
267 return prefixToAddress(address4Factory, ipv4PrefixString(prefix));
270 @Nonnull public final A4NZ ipv4AddressNoZoneFrom(@Nonnull final P4 prefix) {
271 return prefixToAddress(address4NoZoneFactory, ipv4PrefixString(prefix));
274 @Nonnull public final byte[] ipv4AddressBytes(@Nonnull final 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 @Nonnull public final byte[] ipv4AddressNoZoneBytes(@Nonnull final 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[] 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 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final byte[] 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 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final byte[] address, final int mask) {
324 return prefix4Factory.newInstance(prefixStringV4(address, mask));
327 @Nonnull public final P4 ipv4PrefixForShort(@Nonnull final byte[] 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 @Nonnull public final P4 ipv4PrefixForShort(@Nonnull final byte[] 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 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final 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 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final 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 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final A4 addr) {
375 requireNonNull(addr, "Address must not be null");
376 return prefix4Factory.newInstance(stripZone(ipv4AddressString(addr)) + "/32");
379 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final A4 addr, final int mask) {
380 requireNonNull(addr, "Address must not be null");
381 return newIpv4Prefix(stripZone(ipv4AddressString(addr)), mask);
384 @Nonnull public final P4 ipv4PrefixForNoZone(@Nonnull final A4NZ addr) {
385 requireNonNull(addr, "Address must not be null");
386 return prefix4Factory.newInstance(ipv4AddressString(addr) + "/32");
389 @Nonnull public final P4 ipv4PrefixForNoZone(@Nonnull final 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 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 @Nonnull public final Entry<A4, Integer> splitIpv4Prefix(@Nonnull final P4 prefix) {
405 return splitPrefix(address4Factory, ipv4PrefixString(prefix));
408 @Nonnull public final Entry<A4NZ, Integer> splitIpv4PrefixNoZone(@Nonnull final P4 prefix) {
409 return splitPrefix(address4NoZoneFactory, ipv4PrefixString(prefix));
412 @Nonnull public final byte[] ipv4PrefixToBytes(@Nonnull final 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 @Nonnull public final A6 ipv6AddressFor(@Nonnull final byte[] 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 @Nonnull public final A6 ipv6AddressFor(@Nonnull final 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 @Nonnull public final A6NZ ipv6AddressNoZoneFor(@Nonnull final byte[] 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 @Nonnull public final A6NZ ipv6AddressNoZoneFor(@Nonnull final 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 @Nonnull public final A6 ipv6AddressFrom(@Nonnull final P6 prefix) {
475 return prefixToAddress(address6Factory, ipv6PrefixString(prefix));
478 @Nonnull public final A6NZ ipv6AddressNoZoneFrom(@Nonnull final P6 prefix) {
479 return prefixToAddress(address6NoZoneFactory, ipv6PrefixString(prefix));
482 @Nonnull public final byte[] ipv6AddressBytes(@Nonnull final A6 addr) {
483 final String str = ipv6AddressString(addr);
484 final int percent = str.indexOf('%');
485 return ipv6StringBytes(str, percent == -1 ? str.length() : percent);
488 @Nonnull public final byte[] ipv6AddressNoZoneBytes(@Nonnull final A6NZ addr) {
489 final String str = ipv6AddressString(addr);
490 return ipv6StringBytes(str, str.length());
493 private static byte[] ipv6StringBytes(final 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 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final byte[] 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 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final byte[] address, final int mask) {
523 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
524 return prefix6Factory.newInstance(addressStringV6(address) + '/' + mask);
527 @Nonnull public final P6 ipv6PrefixForShort(@Nonnull final byte[] address, final int mask) {
528 return ipv6PrefixForShort(address, 0, mask);
531 @Nonnull public final P6 ipv6PrefixForShort(@Nonnull final byte[] 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 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final 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 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final 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 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final A6 addr) {
577 requireNonNull(addr, "Address must not be null");
578 return prefix6Factory.newInstance(stripZone(ipv6AddressString(addr)) + "/128");
581 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final A6 addr, final int mask) {
582 requireNonNull(addr, "Address must not be null");
583 return newIpv6Prefix(stripZone(ipv6AddressString(addr)), mask);
586 @Nonnull public final P6 ipv6PrefixForNoZone(@Nonnull final A6NZ addr) {
587 requireNonNull(addr, "Address must not be null");
588 return prefix6Factory.newInstance(ipv6AddressString(addr) + "/128");
591 @Nonnull public final P6 ipv6PrefixForNoZone(@Nonnull final 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 @Nonnull public final Entry<A6, Integer> splitIpv6Prefix(@Nonnull final P6 prefix) {
602 return splitPrefix(address6Factory, ipv6PrefixString(prefix));
605 @Nonnull public final Entry<A6NZ, Integer> splitIpv6PrefixNoZone(@Nonnull final P6 prefix) {
606 return splitPrefix(address6NoZoneFactory, ipv6PrefixString(prefix));
609 private static <T> T prefixToAddress(final StringValueObjectFactory<T> factory, final String str) {
610 return factory.newInstance(str.substring(0, str.lastIndexOf('/')));
613 private static <T> Entry<T, Integer> splitPrefix(final StringValueObjectFactory<T> factory, final String str) {
614 final int slash = str.lastIndexOf('/');
615 return new SimpleImmutableEntry<>(factory.newInstance(str.substring(0, slash)),
616 Integer.valueOf(str.substring(slash + 1)));
619 @Nonnull public final byte[] ipv6PrefixToBytes(@Nonnull final P6 prefix) {
620 final String str = ipv6PrefixString(prefix);
621 final byte[] bytes = new byte[INET6_LENGTH + 1];
622 final int slash = str.lastIndexOf('/');
623 Ipv6Utils.fillIpv6Bytes(bytes, str, slash);
624 bytes[INET6_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
628 private static void appendIpv4String(final StringBuilder sb, final byte[] bytes) {
629 checkArgument(bytes.length == INET4_LENGTH, "IPv4 address length is 4 bytes");
631 sb.append(Byte.toUnsignedInt(bytes[0]));
632 for (int i = 1; i < INET4_LENGTH; ++i) {
633 sb.append('.').append(Byte.toUnsignedInt(bytes[i]));
637 private static String addressStringV4(final byte[] bytes) {
638 final StringBuilder sb = new StringBuilder(15);
639 appendIpv4String(sb, bytes);
640 return sb.toString();
643 private static String addressStringV6(final byte[] bytes) {
644 checkArgument(bytes.length == INET6_LENGTH, "IPv6 address length is 16 bytes");
647 return addressStringV6(Inet6Address.getByAddress(bytes));
648 } catch (UnknownHostException e) {
649 throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
653 private static String addressStringV6(final InetAddress addr) {
654 return InetAddresses.toAddrString(addr);
657 private static String prefixStringV4(final byte[] bytes) {
658 final StringBuilder sb = new StringBuilder(18);
659 appendIpv4String(sb, bytes);
660 return sb.append("/32").toString();
663 private static String prefixStringV4(final byte[] bytes, final int mask) {
664 checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
666 final StringBuilder sb = new StringBuilder(18);
667 appendIpv4String(sb, bytes);
668 return sb.append('/').append(mask).toString();
671 private P4 v4PrefixForShort(@Nonnull final byte[] array, final int startOffset, final int size, final int mask) {
672 if (startOffset == 0 && size == INET4_LENGTH && array.length == INET4_LENGTH) {
673 // Easy case, fall back to non-short
674 return ipv4PrefixFor(array, mask);
677 final StringBuilder sb = new StringBuilder(18);
680 sb.append(Byte.toUnsignedInt(array[startOffset]));
681 for (int i = 1; i < size; i++) {
682 sb.append('.').append(Byte.toUnsignedInt(array[startOffset + i]));
686 for (int i = size; i < INET4_LENGTH; i++) {
691 checkArgument(mask > 0 && mask <= 32, "Invalid mask %s", mask);
692 sb.append('/').append(mask);
694 return prefix4Factory.newInstance(sb.toString());