Remove unnecessary version declaration
[mdsal.git] / model / ietf / rfc6991-ietf-inet-types / src / main / java / org / opendaylight / yang / gen / v1 / urn / ietf / params / xml / ns / yang / ietf / inet / types / rev130715 / AbstractIetfInetUtil.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
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.opendaylight.mdsal.binding.spec.reflect.StringValueObjectFactory;
23 import org.opendaylight.mdsal.model.ietf.util.Ipv4Utils;
24 import org.opendaylight.mdsal.model.ietf.util.Ipv6Utils;
25
26 /**
27  * A set of utility methods to efficiently instantiate various ietf-inet-types DTOs.
28  */
29 @Beta
30 @SuppressWarnings("checkstyle:classTypeParameterName")
31 public abstract class AbstractIetfInetUtil {
32     private static final StringValueObjectFactory<Ipv4AddressNoZone> V4NZ_FACTORY =
33         StringValueObjectFactory.create(Ipv4AddressNoZone.class, "0.0.0.0");
34     private static final StringValueObjectFactory<Ipv4Prefix> P4_FACTORY =
35         StringValueObjectFactory.create(Ipv4Prefix.class, "0.0.0.0/0");
36     private static final StringValueObjectFactory<Ipv6AddressNoZone> V6NZ_FACTORY =
37         StringValueObjectFactory.create(Ipv6AddressNoZone.class, "::0");
38     private static final StringValueObjectFactory<Ipv6Prefix> P6_FACTORY =
39         StringValueObjectFactory.create(Ipv6Prefix.class, "::0/0");
40
41     /**
42      * Create an IpAddress by interpreting input bytes as an IPv4 or IPv6 address, based on array length.
43      *
44      * @param bytes 4-byte (IPv4) or 6-byte (IPv6) array
45      * @return An IpAddress object
46      * @throws IllegalArgumentException if bytes has length different from 4 or 6
47      * @throws NullPointerException if bytes is null
48      */
49     public final @NonNull IpAddress ipAddressFor(final byte @NonNull[] bytes) {
50         return switch (bytes.length) {
51             case Ipv4Utils.INET4_LENGTH -> new IpAddress(ipv4AddressFor(bytes));
52             case Ipv6Utils.INET6_LENGTH -> new IpAddress(ipv6AddressFor(bytes));
53             default -> throwInvalidArray(bytes);
54         };
55     }
56
57     public final @NonNull IpAddress ipAddressFor(final @NonNull InetAddress addr) {
58         requireAddress(addr);
59         if (addr instanceof Inet4Address) {
60             return new IpAddress(ipv4AddressFor(addr));
61         } else if (addr instanceof Inet6Address) {
62             return new IpAddress(ipv6AddressFor(addr));
63         } else {
64             throw unhandledAddress(addr);
65         }
66     }
67
68     private static <T> @NonNull T requireAddress(final T addr) {
69         return requireNonNull(addr, "Address must not be null");
70     }
71
72     /**
73      * Create an IpAddress by interpreting input bytes as an IPv4 or IPv6 address, based on array length.
74      *
75      * @param bytes 4-byte (IPv4) or 6-byte (IPv6) array
76      * @return A no-zone IpAddress object
77      * @throws IllegalArgumentException if bytes has length different from 4 or 6
78      * @throws NullPointerException if bytes is null
79      */
80     public final @NonNull IpAddressNoZone ipAddressNoZoneFor(final byte @NonNull[] bytes) {
81         return switch (bytes.length) {
82             case Ipv4Utils.INET4_LENGTH -> new IpAddressNoZone(ipv4AddressFor(bytes));
83             case Ipv6Utils.INET6_LENGTH -> new IpAddressNoZone(ipv6AddressFor(bytes));
84             default -> throwInvalidArray(bytes);
85         };
86     }
87
88     public final @NonNull IpAddressNoZone ipAddressNoZoneFor(final @NonNull InetAddress addr) {
89         requireAddress(addr);
90         if (addr instanceof Inet4Address) {
91             return new IpAddressNoZone(ipv4AddressFor(addr));
92         } else if (addr instanceof Inet6Address) {
93             return new IpAddressNoZone(ipv6AddressFor(addr));
94         } else {
95             throw unhandledAddress(addr);
96         }
97     }
98
99     private static <T> T throwInvalidArray(final byte[] bytes) {
100         throw new IllegalArgumentException("Invalid array length " + bytes.length);
101     }
102
103     private static IllegalArgumentException unhandledAddress(final InetAddress addr) {
104         return new IllegalArgumentException("Unhandled address " + addr);
105     }
106
107     /**
108      * Create an IpPrefix by combining the address with a mask. The address
109      * bytes are interpreted as an address and the specified mask is concatenated to
110      * it. The address bytes are not masked.
111      *
112      * @param bytes Input address as a 4-byte (IPv4) or 16-byte (IPv6) array
113      * @param mask Prefix mask
114      * @return An IpPrefix object
115      * @throws IllegalArgumentException if bytes has length different from 4 or 16 or if mask is not
116      *         in range 0-32 or 0-128 respectively
117      * @throws NullPointerException if bytes is null
118      */
119     public final @NonNull IpPrefix ipPrefixFor(final byte @NonNull[] bytes, final int mask) {
120         return switch (bytes.length) {
121             case Ipv4Utils.INET4_LENGTH -> new IpPrefix(ipv4PrefixFor(bytes, mask));
122             case Ipv6Utils.INET6_LENGTH -> new IpPrefix(ipv6PrefixFor(bytes, mask));
123             default -> throwInvalidArray(bytes);
124         };
125     }
126
127     public final @NonNull IpPrefix ipPrefixFor(final @NonNull InetAddress addr, final int mask) {
128         requireAddress(addr);
129         if (addr instanceof Inet4Address) {
130             return new IpPrefix(ipv4PrefixFor(addr, mask));
131         } else if (addr instanceof Inet6Address) {
132             return new IpPrefix(ipv6PrefixFor(addr, mask));
133         } else {
134             throw unhandledAddress(addr);
135         }
136     }
137
138     public final @NonNull IpPrefix ipPrefixFor(final @NonNull IpAddress addr) {
139         final var v4 = addr.getIpv4Address();
140         return v4 != null ? new IpPrefix(ipv4PrefixFor(v4)) : new IpPrefix(ipv6PrefixFor(coerceIpv6Address(addr)));
141     }
142
143     public final @NonNull IpPrefix ipPrefixForNoZone(final @NonNull IpAddressNoZone addr) {
144         final var v4 = addr.getIpv4AddressNoZone();
145         return v4 != null ? new IpPrefix(ipv4PrefixFor(inet4AddressForNoZone(v4)))
146             : new IpPrefix(ipv6PrefixFor(coerceIpv6AddressNoZone(addr)));
147     }
148
149     public final @NonNull InetAddress inetAddressFor(final @NonNull IpAddress addr) {
150         final var v4 = addr.getIpv4Address();
151         return v4 != null ? inet4AddressFor(v4) : inet6AddressFor(coerceIpv6Address(addr));
152     }
153
154     public final @NonNull InetAddress inetAddressForNoZone(final @NonNull IpAddressNoZone addr) {
155         final var v4 = addr.getIpv4AddressNoZone();
156         return v4 != null ? inet4AddressForNoZone(v4) : inet6AddressForNoZone(coerceIpv6AddressNoZone(addr));
157     }
158
159     public final @NonNull Inet4Address inet4AddressFor(final @NonNull Ipv4Address addr) {
160         try {
161             return (Inet4Address) InetAddress.getByAddress(ipv4AddressBytes(addr));
162         } catch (UnknownHostException e) {
163             throw new IllegalArgumentException("Invalid address " + addr, e);
164         }
165     }
166
167     public final @NonNull Inet4Address inet4AddressForNoZone(final @NonNull Ipv4AddressNoZone addr) {
168         try {
169             return (Inet4Address) InetAddress.getByAddress(ipv4AddressNoZoneBytes(addr));
170         } catch (UnknownHostException e) {
171             throw new IllegalArgumentException("Invalid address " + addr, e);
172         }
173     }
174
175     public final @NonNull Inet6Address inet6AddressFor(final @NonNull Ipv6Address addr) {
176         try {
177             return (Inet6Address) InetAddress.getByAddress(ipv6AddressBytes(addr));
178         } catch (UnknownHostException e) {
179             throw new IllegalArgumentException("Invalid address " + addr, e);
180         }
181     }
182
183     public final @NonNull Inet6Address inet6AddressForNoZone(final @NonNull Ipv6AddressNoZone addr) {
184         try {
185             return (Inet6Address) InetAddress.getByAddress(ipv6AddressNoZoneBytes(addr));
186         } catch (UnknownHostException e) {
187             throw new IllegalArgumentException("Invalid address " + addr, e);
188         }
189     }
190
191     /**
192      * Create an Ipv4AddressNoZone by interpreting input bytes as an IPv4 address.
193      *
194      * @param bytes 4-byte array
195      * @return An Ipv4AddressNoZone object
196      * @throws IllegalArgumentException if bytes has length different from 4
197      * @throws NullPointerException if bytes is null
198      */
199     public final @NonNull Ipv4AddressNoZone ipv4AddressFor(final byte @NonNull[] bytes) {
200         return V4NZ_FACTORY.newInstance(Ipv4Utils.addressString(bytes));
201     }
202
203     /**
204      * Create an Ipv4AddressNoZone by interpreting an {@link Inet4Address}.
205      *
206      * @param addr An {@link Inet4Address}
207      * @return An Ipv4AddressNoZone object
208      * @throws IllegalArgumentException if addr is not an {@link Inet4Address}
209      * @throws NullPointerException if addr is null
210      */
211     public final @NonNull Ipv4AddressNoZone ipv4AddressFor(final @NonNull InetAddress addr) {
212         return V4NZ_FACTORY.newInstance(addressStringV4(addr));
213     }
214
215     /**
216      * Create an Ipv4AddressNoZone by interpreting input 32 bits as an IPv4 address in big-endian format.
217      *
218      * @param bits 32 bits, big endian
219      * @return An Ipv4AddressNoZone object
220      */
221     public final @NonNull Ipv4AddressNoZone ipv4AddressFor(final int bits) {
222         return V4NZ_FACTORY.newInstance(Ipv4Utils.addressString(bits));
223     }
224
225     /**
226      * Create an Ipv4AddressNoZone by interpreting an Ipv4Address.
227      *
228      * @param addr An Ipv4Address
229      * @return An Ipv4AddressNoZone object
230      * @throws NullPointerException if addr is null
231      */
232     public final @NonNull Ipv4AddressNoZone ipv4AddressNoZoneFor(final @NonNull Ipv4Address addr) {
233         requireAddress(addr);
234         return addr instanceof Ipv4AddressNoZone noZone ? noZone
235             :  V4NZ_FACTORY.newInstance(stripZone(addr.getValue()));
236     }
237
238     public final @NonNull Ipv4AddressNoZone ipv4AddressFrom(final @NonNull Ipv4Prefix prefix) {
239         return prefixToAddress(V4NZ_FACTORY, prefix.getValue());
240     }
241
242     public final byte @NonNull[] ipv4AddressBytes(final @NonNull Ipv4Address addr) {
243         /*
244          * This implementation relies heavily on the input string having been validated to comply with
245          * the Ipv4Address pattern, which may include a zone index.
246          */
247         final var str = addr.getValue();
248         final int percent = str.indexOf('%');
249         return Ipv4Utils.addressBytes(str, percent == -1 ? str.length() : percent);
250     }
251
252     public final int ipv4AddressBits(final @NonNull Ipv4Address addr) {
253         final var str = addr.getValue();
254         final int percent = str.indexOf('%');
255         return Ipv4Utils.addressBits(str, percent == -1 ? str.length() : percent);
256     }
257
258     public final byte @NonNull[] ipv4AddressNoZoneBytes(final @NonNull Ipv4AddressNoZone addr) {
259         /*
260          * This implementation relies heavily on the input string having been validated to comply with
261          * the Ipv4AddressNoZone pattern, which must not include a zone index.
262          */
263         final String str = addr.getValue();
264         return Ipv4Utils.addressBytes(str, str.length());
265     }
266
267     public final int ipv4AddressNoZoneBits(final @NonNull Ipv4AddressNoZone addr) {
268         final var str = addr.getValue();
269         return Ipv4Utils.addressBits(str, str.length());
270     }
271
272     /**
273      * Create a /32 Ipv4Prefix by interpreting input bytes as an IPv4 address.
274      *
275      * @param bytes four-byte array
276      * @return An Ipv4Prefix object
277      * @throws IllegalArgumentException if bytes has length different from 4
278      * @throws NullPointerException if bytes is null
279      */
280     public final @NonNull Ipv4Prefix ipv4PrefixFor(final byte @NonNull[] bytes) {
281         return P4_FACTORY.newInstance(prefixStringV4(bytes));
282     }
283
284     /**
285      * Create a Ipv4Prefix by combining the address with a mask. The address
286      * bytes are interpreted as an address and the specified mask is concatenated to
287      * it. The address bytes are not masked, hence input <code>address = { 1, 2, 3, 4 }</code>
288      * and <code>mask=24</code> will result in <code>1.2.3.4/24</code>.
289      *
290      * @param address Input address as a 4-byte array
291      * @param mask Prefix mask
292      * @return An Ipv4Prefix object
293      * @throws IllegalArgumentException if bytes has length different from 4 or if mask is not in range 0-32
294      * @throws NullPointerException if bytes is null
295      */
296     public final @NonNull Ipv4Prefix ipv4PrefixFor(final byte @NonNull[] address, final int mask) {
297         return P4_FACTORY.newInstance(prefixStringV4(address, mask));
298     }
299
300     /**
301      * Create a /32 Ipv4Prefix for an {@link Inet4Address}.
302      *
303      * @param addr An {@link Inet4Address}
304      * @return An Ipv4Prefix object
305      * @throws IllegalArgumentException if addr is not an Inet4Address
306      * @throws NullPointerException if addr is null
307      */
308     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull InetAddress addr) {
309         return P4_FACTORY.newInstance(addressStringV4(addr) + "/32");
310     }
311
312     /**
313      * Create a Ipv4Prefix by combining the address with a mask. The address bytes are not masked.
314      *
315      * @param addr An {@link Inet4Address}
316      * @param mask Prefix mask
317      * @return An Ipv4Prefix object
318      * @throws IllegalArgumentException if addr is not an Inet4Address or if mask is not in range 0-32
319      * @throws NullPointerException if addr is null
320      */
321     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull InetAddress addr, final int mask) {
322         return newIpv4Prefix(addressStringV4(addr), mask);
323     }
324
325     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull Ipv4Address addr) {
326         return P4_FACTORY.newInstance(stripZone(addr.getValue()) + "/32");
327     }
328
329     public final @NonNull Ipv4Prefix ipv4PrefixFor(final @NonNull Ipv4Address addr, final int mask) {
330         return newIpv4Prefix(stripZone(addr.getValue()), mask);
331     }
332
333     public final @NonNull Ipv4Prefix ipv4PrefixForNoZone(final @NonNull Ipv4AddressNoZone addr) {
334         return P4_FACTORY.newInstance(addr.getValue() + "/32");
335     }
336
337     public final @NonNull Ipv4Prefix ipv4PrefixForNoZone(final @NonNull Ipv4AddressNoZone addr, final int mask) {
338         return newIpv4Prefix(addr.getValue(), mask);
339     }
340
341     public final @NonNull Ipv4Prefix ipv4PrefixForShort(final byte @NonNull[] address, final int mask) {
342         if (mask == 0) {
343             // Easy case, reuse the template
344             return P4_FACTORY.getTemplate();
345         }
346
347         return v4PrefixForShort(address, 0, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
348     }
349
350     public final @NonNull Ipv4Prefix ipv4PrefixForShort(final byte @NonNull[] array, final int startOffset,
351             final int mask) {
352         if (mask == 0) {
353             // Easy case, reuse the template
354             return P4_FACTORY.getTemplate();
355         }
356
357         return v4PrefixForShort(array, startOffset, mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1), mask);
358     }
359
360     private static String stripZone(final String str) {
361         final int percent = str.indexOf('%');
362         return percent == -1 ? str : str.substring(0, percent);
363     }
364
365     private @NonNull Ipv4Prefix newIpv4Prefix(final String addr, final int mask) {
366         checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
367         return P4_FACTORY.newInstance(addr + '/' + mask);
368     }
369
370     public final @NonNull Entry<Ipv4AddressNoZone, Integer> splitIpv4Prefix(final @NonNull Ipv4Prefix prefix) {
371         return splitPrefix(V4NZ_FACTORY, prefix.getValue());
372     }
373
374     public final byte @NonNull[] ipv4PrefixToBytes(final @NonNull Ipv4Prefix prefix) {
375         final var str = prefix.getValue();
376         final int slash = str.lastIndexOf('/');
377
378         final byte[] bytes = new byte[Ipv4Utils.INET4_LENGTH + 1];
379         Ipv4Utils.fillIpv4Bytes(bytes, 0, str, 0, slash);
380         bytes[Ipv4Utils.INET4_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
381         return bytes;
382     }
383
384     /**
385      * Create an Ipv6Address by interpreting input bytes as an IPv6 address.
386      *
387      * @param bytes 16-byte array
388      * @return An Ipv6Address object
389      * @throws IllegalArgumentException if bytes has length different from 16
390      * @throws NullPointerException if bytes is null
391      */
392     public final @NonNull Ipv6AddressNoZone ipv6AddressFor(final byte @NonNull[] bytes) {
393         return V6NZ_FACTORY.newInstance(addressStringV6(bytes));
394     }
395
396     /**
397      * Create an Ipv6Address by interpreting an {@link Inet6Address}.
398      *
399      * @param addr An {@link Inet6Address}
400      * @return An Ipv6Address object
401      * @throws IllegalArgumentException if addr is not an {@link Inet6Address}
402      * @throws NullPointerException if addr is null
403      */
404     public final @NonNull Ipv6AddressNoZone ipv6AddressFor(final @NonNull InetAddress addr) {
405         return V6NZ_FACTORY.newInstance(addressStringV6(addr));
406     }
407
408     /**
409      * Create an Ipv6AddressNoZone by interpreting an Ipv6Address.
410      *
411      * @param addr An Ipv6Address
412      * @return An Ipv6AddressNoZone object
413      * @throws NullPointerException if addr is null
414      */
415     public final @NonNull Ipv6AddressNoZone ipv6AddressNoZoneFor(final @NonNull Ipv6Address addr) {
416         requireAddress(addr);
417         return addr instanceof Ipv6AddressNoZone noZone ? noZone
418                 : V6NZ_FACTORY.newInstance(stripZone(addr.getValue()));
419     }
420
421     public final @NonNull Ipv6AddressNoZone ipv6AddressFrom(final @NonNull Ipv6Prefix prefix) {
422         return prefixToAddress(V6NZ_FACTORY, prefix.getValue());
423     }
424
425     public final byte @NonNull[] ipv6AddressBytes(final @NonNull Ipv6Address addr) {
426         final var str = addr.getValue();
427         final int percent = str.indexOf('%');
428         return ipv6StringBytes(str, percent == -1 ? str.length() : percent);
429     }
430
431     public final byte @NonNull[] ipv6AddressNoZoneBytes(final @NonNull Ipv6Address addr) {
432         final var str = addr.getValue();
433         return ipv6StringBytes(str, str.length());
434     }
435
436     private static byte @NonNull[] ipv6StringBytes(final @NonNull String str, final int limit) {
437         final byte[] bytes = new byte[Ipv6Utils.INET6_LENGTH];
438         Ipv6Utils.fillIpv6Bytes(bytes, str, limit);
439         return bytes;
440     }
441
442     /**
443      * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
444      *
445      * @param bytes four-byte array
446      * @return An Ipv6Prefix object
447      * @throws IllegalArgumentException if bytes has length different from 16
448      * @throws NullPointerException if bytes is null
449      */
450     public final @NonNull Ipv6Prefix ipv6PrefixFor(final byte @NonNull[] bytes) {
451         return P6_FACTORY.newInstance(addressStringV6(bytes) + "/128");
452     }
453
454     /**
455      * Create a Ipv6Prefix by combining the address with a mask. The address
456      * bytes are interpreted as an address and the specified mask is concatenated to
457      * it. The address bytes are not masked.
458      *
459      * @param address Input address as a 16-byte array
460      * @param mask Prefix mask
461      * @return An Ipv6Prefix object
462      * @throws IllegalArgumentException if bytes has length different from 16 or if mask is not in range 0-128
463      * @throws NullPointerException if bytes is null
464      */
465     public final @NonNull Ipv6Prefix ipv6PrefixFor(final byte @NonNull[] address, final int mask) {
466         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
467         return P6_FACTORY.newInstance(addressStringV6(address) + '/' + mask);
468     }
469
470     /**
471      * Create a /128 Ipv6Prefix by interpreting input bytes as an IPv6 address.
472      *
473      * @param addr an {@link Inet6Address}
474      * @return An Ipv6Prefix object
475      * @throws IllegalArgumentException if addr is not an Inet6Address
476      * @throws NullPointerException if addr is null
477      */
478     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull InetAddress addr) {
479         return P6_FACTORY.newInstance(addressStringV6(addr) + "/128");
480     }
481
482     /**
483      * Create a Ipv6Prefix by combining the address with a mask. The address
484      * bytes are interpreted as an address and the specified mask is concatenated to
485      * it. The address bytes are not masked.
486      *
487      * @param addr Input address
488      * @param mask Prefix mask
489      * @return An Ipv6Prefix object
490      * @throws IllegalArgumentException if addr is not an Inet6Address or if mask is not in range 0-128
491      * @throws NullPointerException if addr is null
492      */
493     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull InetAddress addr, final int mask) {
494         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
495         return P6_FACTORY.newInstance(addressStringV6(addr) + '/' + mask);
496     }
497
498     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull Ipv6Address addr) {
499         return P6_FACTORY.newInstance(stripZone(addr.getValue()) + "/128");
500     }
501
502     public final @NonNull Ipv6Prefix ipv6PrefixFor(final @NonNull Ipv6Address addr, final int mask) {
503         return newIpv6Prefix(stripZone(addr.getValue()), mask);
504     }
505
506     public final @NonNull Ipv6Prefix ipv6PrefixForNoZone(final @NonNull Ipv6AddressNoZone addr) {
507         return P6_FACTORY.newInstance(addr.getValue() + "/128");
508     }
509
510     public final @NonNull Ipv6Prefix ipv6PrefixForNoZone(final @NonNull Ipv6AddressNoZone addr, final int mask) {
511         return newIpv6Prefix(addr.getValue(), mask);
512     }
513
514     public final @NonNull Ipv6Prefix ipv6PrefixForShort(final byte @NonNull[] address, final int mask) {
515         return ipv6PrefixForShort(address, 0, mask);
516     }
517
518     public final @NonNull Ipv6Prefix ipv6PrefixForShort(final byte @NonNull[] array, final int startOffset,
519             final int mask) {
520         if (mask == 0) {
521             // Easy case, reuse the template
522             return P6_FACTORY.getTemplate();
523         }
524
525         checkArgument(mask > 0 && mask <= 128, "Invalid mask %s", mask);
526         final int size = mask / Byte.SIZE + (mask % Byte.SIZE == 0 ? 0 : 1);
527
528         // Until we can instantiate an IPv6 address for a partial array, use a temporary buffer
529         byte[] tmp = new byte[Ipv6Utils.INET6_LENGTH];
530         System.arraycopy(array, startOffset, tmp, 0, size);
531         return ipv6PrefixFor(tmp, mask);
532     }
533
534     private Ipv6Prefix newIpv6Prefix(final String addr, final int mask) {
535         checkArgument(mask >= 0 && mask <= 128, "Invalid mask %s", mask);
536         return P6_FACTORY.newInstance(addr + '/' + mask);
537     }
538
539     public final @NonNull Entry<Ipv6AddressNoZone, Integer> splitIpv6Prefix(final @NonNull Ipv6Prefix prefix) {
540         return splitPrefix(V6NZ_FACTORY, prefix.getValue());
541     }
542
543     private static <T> @NonNull T prefixToAddress(final StringValueObjectFactory<T> factory, final String str) {
544         return factory.newInstance(str.substring(0, str.lastIndexOf('/')));
545     }
546
547     private static <T> @NonNull Entry<T, Integer> splitPrefix(final StringValueObjectFactory<T> factory,
548             final String str) {
549         final int slash = str.lastIndexOf('/');
550         return new SimpleImmutableEntry<>(factory.newInstance(str.substring(0, slash)),
551                 Integer.valueOf(str.substring(slash + 1)));
552     }
553
554     public final byte @NonNull[] ipv6PrefixToBytes(final @NonNull Ipv6Prefix prefix) {
555         final var str = prefix.getValue();
556         final byte[] bytes = new byte[Ipv6Utils.INET6_LENGTH + 1];
557         final int slash = str.lastIndexOf('/');
558         Ipv6Utils.fillIpv6Bytes(bytes, str, slash);
559         bytes[Ipv6Utils.INET6_LENGTH] = (byte)Integer.parseInt(str.substring(slash + 1), 10);
560         return bytes;
561     }
562
563     private static @NonNull String addressStringV4(final InetAddress addr) {
564         requireAddress(addr);
565         checkArgument(addr instanceof Inet4Address, "Address has to be an Inet4Address");
566         return addr.getHostAddress();
567     }
568
569     private static String addressStringV6(final byte @NonNull[] bytes) {
570         checkArgument(bytes.length == Ipv6Utils.INET6_LENGTH, "IPv6 address length is 16 bytes");
571
572         try {
573             return addressStringV6(Inet6Address.getByAddress(null, bytes, null));
574         } catch (UnknownHostException e) {
575             throw new IllegalArgumentException(String.format("Invalid input %s", bytes), e);
576         }
577     }
578
579     private static String addressStringV6(final InetAddress addr) {
580         requireAddress(addr);
581         checkArgument(addr instanceof Inet6Address, "Address has to be an Inet6Address");
582         return addressStringV6((Inet6Address) addr);
583     }
584
585     private static String addressStringV6(final Inet6Address addr) {
586         return InetAddresses.toAddrString(addr);
587     }
588
589     private static String prefixStringV4(final byte @NonNull[] bytes) {
590         final StringBuilder sb = new StringBuilder(18);
591         Ipv4Utils.appendIpv4String(sb, bytes);
592         return sb.append("/32").toString();
593     }
594
595     private static String prefixStringV4(final byte @NonNull[] bytes, final int mask) {
596         checkArgument(mask >= 0 && mask <= 32, "Invalid mask %s", mask);
597
598         final StringBuilder sb = new StringBuilder(18);
599         Ipv4Utils.appendIpv4String(sb, bytes);
600         return sb.append('/').append(mask).toString();
601     }
602
603     private @NonNull Ipv4Prefix v4PrefixForShort(final byte @NonNull[] array, final int startOffset, final int size,
604             final int mask) {
605         if (startOffset == 0 && size == Ipv4Utils.INET4_LENGTH && array.length == Ipv4Utils.INET4_LENGTH) {
606             // Easy case, fall back to non-short
607             return ipv4PrefixFor(array, mask);
608         }
609
610         final StringBuilder sb = new StringBuilder(18);
611
612         // Add from address
613         sb.append(Byte.toUnsignedInt(array[startOffset]));
614         for (int i = 1; i < size; i++) {
615             sb.append('.').append(Byte.toUnsignedInt(array[startOffset + i]));
616         }
617
618         // Add zeros
619         for (int i = size; i < Ipv4Utils.INET4_LENGTH; i++) {
620             sb.append(".0");
621         }
622
623         // Add mask
624         checkArgument(mask > 0 && mask <= 32, "Invalid mask %s", mask);
625         sb.append('/').append(mask);
626
627         return P4_FACTORY.newInstance(sb.toString());
628     }
629
630     private static @NonNull Ipv6Address coerceIpv6Address(final @NonNull IpAddress addr) {
631         final var ret = addr.getIpv6Address();
632         checkArgument(ret != null, "Address %s is neither IPv4 nor IPv6", addr);
633         return ret;
634     }
635
636     private static @NonNull Ipv6AddressNoZone coerceIpv6AddressNoZone(final @NonNull IpAddressNoZone addr) {
637         final var ret = addr.getIpv6AddressNoZone();
638         checkArgument(ret != null, "Address %s is neither IPv4 nor IPv6", addr);
639         return ret;
640     }
641 }