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 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 @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");
359 @Nonnull public final String stringForIpAddress(@Nonnull final A addr) {
360 final A4 ipv4 = maybeIpv4Address(addr);
362 return ipv4AddressString(ipv4);
364 final A6 v6 = maybeIpv6Address(addr);
365 checkArgument(v6 != null, "Address %s is neither IPv4 nor IPv6", addr);
366 return ipv6AddressString(v6);
369 @Nonnull public final String stringForIpAddressNoZone(@Nonnull final ANZ addr) {
370 final A4NZ v4 = maybeIpv4AddressNoZone(addr);
372 return ipv4AddressString(v4);
374 final A6NZ v6 = maybeIpv6AddressNoZone(addr);
375 checkArgument(v6 != null, "Address %s is neither IPv4 nor IPv6", addr);
376 return ipv6AddressString(v6);
380 * Create a Ipv4Prefix by combining the address with a mask. The address bytes are not masked.
382 * @param addr An {@link Inet4Address}
383 * @param mask Prefix mask
384 * @return An Ipv4Prefix object
385 * @throws IllegalArgumentException if addr is not an Inet4Address or if mask is not in range 0-32
386 * @throws NullPointerException if addr is null
388 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final InetAddress addr, final int mask) {
389 requireNonNull(addr, "Address must not be null");
390 checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
391 return newIpv4Prefix(addr.getHostAddress(), mask);
394 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final A4 addr) {
395 requireNonNull(addr, "Address must not be null");
396 return prefix4Factory.newInstance(stripZone(ipv4AddressString(addr)) + "/32");
399 @Nonnull public final P4 ipv4PrefixFor(@Nonnull final A4 addr, final int mask) {
400 requireNonNull(addr, "Address must not be null");
401 return newIpv4Prefix(stripZone(ipv4AddressString(addr)), mask);
404 @Nonnull public final P4 ipv4PrefixForNoZone(@Nonnull final A4NZ addr) {
405 requireNonNull(addr, "Address must not be null");
406 return prefix4Factory.newInstance(ipv4AddressString(addr) + "/32");
409 @Nonnull public final P4 ipv4PrefixForNoZone(@Nonnull final A4NZ addr, final int mask) {
410 requireNonNull(addr, "Address must not be null");
411 return newIpv4Prefix(ipv4AddressString(addr), mask);
414 private static String stripZone(final String str) {
415 final int percent = str.indexOf('%');
416 return percent == -1 ? str : str.substring(0, percent);
419 private P4 newIpv4Prefix(final String addr, final int mask) {
420 checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
421 return prefix4Factory.newInstance(addr + '/' + mask);
424 @Nonnull public final Entry<A4, Integer> splitIpv4Prefix(@Nonnull final P4 prefix) {
425 return splitPrefix(address4Factory, ipv4PrefixString(prefix));
428 @Nonnull public final Entry<A4NZ, Integer> splitIpv4PrefixNoZone(@Nonnull final P4 prefix) {
429 return splitPrefix(address4NoZoneFactory, ipv4PrefixString(prefix));
432 @Nonnull public final byte[] ipv4PrefixToBytes(@Nonnull final P4 prefix) {
433 final String str = ipv4PrefixString(prefix);
434 final int slash = str.lastIndexOf('/');
436 final byte[] bytes = new byte[INET4_LENGTH + 1];
437 Ipv4Utils.fillIpv4Bytes(bytes, 0, str, 0, slash);
438 bytes[INET4_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
443 * Create an Ipv6Address by interpreting input bytes as an IPv6 address.
445 * @param bytes 16-byte array
446 * @return An Ipv6Address object
447 * @throws IllegalArgumentException if bytes has length different from 16
448 * @throws NullPointerException if bytes is null
450 @Nonnull public final A6 ipv6AddressFor(@Nonnull final byte[] bytes) {
451 return address6Factory.newInstance(addressStringV6(bytes));
455 * Create an Ipv6Address by interpreting an {@link Inet6Address}.
457 * @param addr An {@link Inet6Address}
458 * @return An Ipv6Address object
459 * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
460 * @throws NullPointerException if addr is null
462 @Nonnull public final A6 ipv6AddressFor(@Nonnull final InetAddress addr) {
463 requireNonNull(addr, "Address must not be null");
464 checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
465 return address6Factory.newInstance(addressStringV6(addr));
469 * Create an Ipv6AddressNoZone by interpreting input bytes as an IPv6 address.
471 * @param bytes 16-byte array
472 * @return An Ipv6Address object
473 * @throws IllegalArgumentException if bytes has length different from 16
474 * @throws NullPointerException if bytes is null
476 @Nonnull public final A6NZ ipv6AddressNoZoneFor(@Nonnull final byte[] bytes) {
477 return address6NoZoneFactory.newInstance(addressStringV6(bytes));
481 * Create an Ipv6AddressNoZone by interpreting an {@link Inet6Address}.
483 * @param addr An {@link Inet6Address}
484 * @return An Ipv6AddressNoZone object
485 * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
486 * @throws NullPointerException if addr is null
488 @Nonnull public final A6NZ ipv6AddressNoZoneFor(@Nonnull final InetAddress addr) {
489 requireNonNull(addr, "Address must not be null");
490 checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
491 return address6NoZoneFactory.newInstance(addressStringV6(addr));
494 @Nonnull public final A6 ipv6AddressFrom(@Nonnull final P6 prefix) {
495 return prefixToAddress(address6Factory, ipv6PrefixString(prefix));
498 @Nonnull public final A6NZ ipv6AddressNoZoneFrom(@Nonnull final P6 prefix) {
499 return prefixToAddress(address6NoZoneFactory, ipv6PrefixString(prefix));
502 @Nonnull public final byte[] ipv6AddressBytes(@Nonnull final A6 addr) {
503 final String str = ipv6AddressString(addr);
504 final int percent = str.indexOf('%');
505 return ipv6StringBytes(str, percent == -1 ? str.length() : percent);
508 @Nonnull public final byte[] ipv6AddressNoZoneBytes(@Nonnull final A6NZ addr) {
509 final String str = ipv6AddressString(addr);
510 return ipv6StringBytes(str, str.length());
513 private static byte[] ipv6StringBytes(final String str, final int limit) {
514 final byte[] bytes = new byte[INET6_LENGTH];
515 Ipv6Utils.fillIpv6Bytes(bytes, str, limit);
520 * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
522 * @param bytes four-byte array
523 * @return An Ipv6Prefix object
524 * @throws IllegalArgumentException if bytes has length different from 16
525 * @throws NullPointerException if bytes is null
527 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final byte[] bytes) {
528 return prefix6Factory.newInstance(addressStringV6(bytes) + "/128");
532 * Create a Ipv6Prefix by combining the address with a mask. The address
533 * bytes are interpreted as an address and the specified mask is concatenated to
534 * it. The address bytes are not masked.
536 * @param address Input address as a 16-byte array
537 * @param mask Prefix mask
538 * @return An Ipv6Prefix object
539 * @throws IllegalArgumentException if bytes has length different from 16 or if mask is not in range 0-128
540 * @throws NullPointerException if bytes is null
542 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final byte[] address, final int mask) {
543 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
544 return prefix6Factory.newInstance(addressStringV6(address) + '/' + mask);
547 @Nonnull public final P6 ipv6PrefixForShort(@Nonnull final byte[] address, final int mask) {
548 return ipv6PrefixForShort(address, 0, mask);
551 @Nonnull public final P6 ipv6PrefixForShort(@Nonnull final byte[] array, final int startOffset, final int mask) {
553 // Easy case, reuse the template
554 return prefix6Factory.getTemplate();
557 checkArgument(mask > 0 && mask <= 128, "Invalid mask %s", mask);
558 final int size = mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1);
560 // Until we can instantiate an IPv6 address for a partial array, use a temporary buffer
561 byte[] tmp = new byte[INET6_LENGTH];
562 System.arraycopy(array, startOffset, tmp, 0, size);
563 return ipv6PrefixFor(tmp, mask);
567 * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv4 address.
569 * @param addr an {@link Inet6Address}
570 * @return An Ipv6Prefix object
571 * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
572 * @throws NullPointerException if addr is null
574 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final InetAddress addr) {
575 return prefix6Factory.newInstance(addressStringV6(addr) + "/128");
579 * Create a Ipv6Prefix by combining the address with a mask. The address
580 * bytes are interpreted as an address and the specified mask is concatenated to
581 * it. The address bytes are not masked.
583 * @param addr Input address
584 * @param mask Prefix mask
585 * @return An Ipv6Prefix object
586 * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
587 * @throws NullPointerException if addr is null
589 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final InetAddress addr, final int mask) {
590 requireNonNull(addr, "Address must not be null");
591 checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
592 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
593 return prefix6Factory.newInstance(addressStringV6(addr) + '/' + mask);
596 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final A6 addr) {
597 requireNonNull(addr, "Address must not be null");
598 return prefix6Factory.newInstance(stripZone(ipv6AddressString(addr)) + "/128");
601 @Nonnull public final P6 ipv6PrefixFor(@Nonnull final A6 addr, final int mask) {
602 requireNonNull(addr, "Address must not be null");
603 return newIpv6Prefix(stripZone(ipv6AddressString(addr)), mask);
606 @Nonnull public final P6 ipv6PrefixForNoZone(@Nonnull final A6NZ addr) {
607 requireNonNull(addr, "Address must not be null");
608 return prefix6Factory.newInstance(ipv6AddressString(addr) + "/128");
611 @Nonnull public final P6 ipv6PrefixForNoZone(@Nonnull final A6NZ addr, final int mask) {
612 requireNonNull(addr, "Address must not be null");
613 return newIpv6Prefix(ipv6AddressString(addr), mask);
616 private P6 newIpv6Prefix(final String addr, final int mask) {
617 checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
618 return prefix6Factory.newInstance(addr + '/' + mask);
621 @Nonnull public final Entry<A6, Integer> splitIpv6Prefix(@Nonnull final P6 prefix) {
622 return splitPrefix(address6Factory, ipv6PrefixString(prefix));
625 @Nonnull public final Entry<A6NZ, Integer> splitIpv6PrefixNoZone(@Nonnull final P6 prefix) {
626 return splitPrefix(address6NoZoneFactory, ipv6PrefixString(prefix));
629 private static <T> T prefixToAddress(final StringValueObjectFactory<T> factory, final String str) {
630 return factory.newInstance(str.substring(0, str.lastIndexOf('/')));
633 private static <T> Entry<T, Integer> splitPrefix(final StringValueObjectFactory<T> factory, final String str) {
634 final int slash = str.lastIndexOf('/');
635 return new SimpleImmutableEntry<>(factory.newInstance(str.substring(0, slash)),
636 Integer.valueOf(str.substring(slash + 1)));
639 @Nonnull public final byte[] ipv6PrefixToBytes(@Nonnull final P6 prefix) {
640 final String str = ipv6PrefixString(prefix);
641 final byte[] bytes = new byte[INET6_LENGTH + 1];
642 final int slash = str.lastIndexOf('/');
643 Ipv6Utils.fillIpv6Bytes(bytes, str, slash);
644 bytes[INET6_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
648 private static void appendIpv4String(final StringBuilder sb, final byte[] bytes) {
649 checkArgument(bytes.length == INET4_LENGTH, "IPv4 address length is 4 bytes");
651 sb.append(Byte.toUnsignedInt(bytes[0]));
652 for (int i = 1; i < INET4_LENGTH; ++i) {
653 sb.append('.').append(Byte.toUnsignedInt(bytes[i]));
657 private static String addressStringV4(final byte[] bytes) {
658 final StringBuilder sb = new StringBuilder(15);
659 appendIpv4String(sb, bytes);
660 return sb.toString();
663 private static String addressStringV6(final byte[] bytes) {
664 checkArgument(bytes.length == INET6_LENGTH, "IPv6 address length is 16 bytes");
667 return addressStringV6(Inet6Address.getByAddress(bytes));
668 } catch (UnknownHostException e) {
669 throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
673 private static String addressStringV6(final InetAddress addr) {
674 return InetAddresses.toAddrString(addr);
677 private static String prefixStringV4(final byte[] bytes) {
678 final StringBuilder sb = new StringBuilder(18);
679 appendIpv4String(sb, bytes);
680 return sb.append("/32").toString();
683 private static String prefixStringV4(final byte[] bytes, final int mask) {
684 checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
686 final StringBuilder sb = new StringBuilder(18);
687 appendIpv4String(sb, bytes);
688 return sb.append('/').append(mask).toString();
691 private P4 v4PrefixForShort(@Nonnull final byte[] array, final int startOffset, final int size, final int mask) {
692 if (startOffset == 0 && size == INET4_LENGTH && array.length == INET4_LENGTH) {
693 // Easy case, fall back to non-short
694 return ipv4PrefixFor(array, mask);
697 final StringBuilder sb = new StringBuilder(18);
700 sb.append(Byte.toUnsignedInt(array[startOffset]));
701 for (int i = 1; i < size; i++) {
702 sb.append('.').append(Byte.toUnsignedInt(array[startOffset + i]));
706 for (int i = size; i < INET4_LENGTH; i++) {
711 checkArgument(mask > 0 && mask <= 32, "Invalid mask %s", mask);
712 sb.append('/').append(mask);
714 return prefix4Factory.newInstance(sb.toString());