-
Notifications
You must be signed in to change notification settings - Fork 74
Code Examples 4: Converting to and from Other Formats
- Produce a Variety of Strings of an Address for Text Search
- Convert to Binary String
- Convert to/from IPv6 Address from/to MAC Address
- Convert to/from IPv6 Address from/to Ascii Base 85 Encoding
- Convert Subnet Members to Integers using Filter, Map and Collect Stream Operations
- Extend Address Classes for Customized Conversion to/from IPv4 Address from/to IPv6 Address
- Write/Read Addresses to/from Direct Byte Buffer in Native Byte Order
strings("ffff::a000:0/108");
strings("ffff::8fff:ffff:0:ffff");
static void strings(String str) {
IPAddress addr = new IPAddressString(str).getAddress();
String strings[] = addr.toStandardStrings();
System.out.println(strings.length + " strings:\n" +
String.join(",\n", strings) + '\n');
}Output:
18 strings:
ffff:0000:0000:0000:0000:0000:160.000.000.000/108,
FFFF:0:0:0:0:0:160.000.000.000/108,
FFFF:0:0:0:0:0:A000:0/108,
ffff::a000:0/108,
FFFF::160.000.000.000/108,
FFFF:0000:0000:0000:0000:0000:160.000.000.000/108,
FFFF:0000:0000:0000:0000:0000:160.0.0.0/108,
ffff::160.0.0.0/108,
FFFF::160.0.0.0/108,
ffff:0:0:0:0:0:160.000.000.000/108,
FFFF::A000:0/108,
ffff:0000:0000:0000:0000:0000:160.0.0.0/108,
FFFF:0:0:0:0:0:160.0.0.0/108,
ffff:0:0:0:0:0:a000:0/108,
ffff:0000:0000:0000:0000:0000:a000:0000/108,
ffff::160.000.000.000/108,
FFFF:0000:0000:0000:0000:0000:A000:0000/108,
ffff:0:0:0:0:0:160.0.0.0/108
24 strings:
FFFF:0000:0000:0000:8FFF:FFFF:0000:FFFF,
ffff::8fff:ffff:0:ffff,
ffff:0:0:0:8fff:ffff:000.000.255.255,
FFFF::8FFF:FFFF:000.000.255.255,
FFFF:0000:0000:0000:8FFF:FFFF:000.000.255.255,
ffff:0000:0000:0000:8fff:ffff:0000:ffff,
ffff:0:0:0:8fff:ffff:0.0.255.255,
ffff:0:0:0:8fff:ffff:0:ffff,
ffff:0000:0000:0000:8fff:ffff:000.000.255.255,
FFFF:0000:0000:0000:8FFF:FFFF::FFFF,
FFFF:0000:0000:0000:8FFF:FFFF:0.0.255.255,
ffff::8fff:ffff:0000:ffff,
FFFF:0:0:0:8FFF:FFFF:0.0.255.255,
ffff:0000:0000:0000:8fff:ffff:0.0.255.255,
FFFF:0:0:0:8FFF:FFFF:000.000.255.255,
ffff::8fff:ffff:0.0.255.255,
FFFF:0:0:0:8FFF:FFFF:0:FFFF,
ffff:0:0:0:8fff:ffff::ffff,
FFFF:0:0:0:8FFF:FFFF::FFFF,
FFFF::8FFF:FFFF:0:FFFF,
FFFF::8FFF:FFFF:0000:FFFF,
ffff:0000:0000:0000:8fff:ffff::ffff,
ffff::8fff:ffff:000.000.255.255,
FFFF::8FFF:FFFF:0.0.255.255
String str = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
IPAddressString addrStr = new IPAddressString(str);
IPAddress addr = addrStr.getAddress();
String binaryStr = addr.toBinaryString();
System.out.println(binaryStr);Output:
00100000000000010000110110111000100001011010001100000000000000000000000000000000100010100010111000000011011100000111001100110100
// start with a /64 prefix and a mac address
IPv6Address subnet = new IPAddressString("1111:2222:3333:4444::/64").
getAddress().toIPv6();
MACAddress mac = new MACAddressString("aa:bb:cc:dd:ee:ff").getAddress();
// break into the components and combine ourselves
IPv6AddressSection prefix = subnet.getNetworkSection();
IPv6AddressSection macConverted = mac.toEUI64IPv6();
IPv6Address converted = new IPv6Address(prefix.append(macConverted));
System.out.println("combined " + subnet + " with " + mac + " resulting in " +
converted);
// or use a shortcut
IPv6Address convertedAgain = new IPv6Address(subnet, mac);
System.out.println("combined " + subnet + " with " + mac + " resulting in " +
convertedAgain);
// back to mac again
MACAddress macAgain = converted.toEUI(false);
System.out.println("extracted " + macAgain);
// convert to the link-local IPv6 address
IPv6Address linkLocal = macAgain.toLinkLocalIPv6();
System.out.println("converted " + mac + " to link local " + linkLocal);Output:
combined 1111:2222:3333:4444::/64 with aa:bb:cc:dd:ee:ff resulting in 1111:2222:3333:4444:a8bb:ccff:fedd:eeff/64
combined 1111:2222:3333:4444::/64 with aa:bb:cc:dd:ee:ff resulting in 1111:2222:3333:4444:a8bb:ccff:fedd:eeff/64
extracted aa:bb:cc:dd:ee:ff
converted aa:bb:cc:dd:ee:ff to link local fe80::a8bb:ccff:fedd:eeff
Storing an IPv6 address, with no scope or zone, takes 16 bytes. When using ascii chars with conventional notation, it takes anywhere from two bytes ("::") to 45 bytes ("aaaa:bbbb:cccc:dddd:eeee:ffff:255.255.255.255"), typically extending to 39 bytes ("aaa:bbbb:cccc:dddd:eeee:ffff:aaaa:bbbb"). Using a base 85 ascii string takes 20 bytes, not much more than using bytes directly.
IPv6Address addr = new IPAddressString("102:304:506:708:90a:b0c:d0e:fff").
toAddress().toIPv6();
// to bytes and back
bytes = addr.getBytes();
addr = new IPv6Address(bytes);
print(addr, bytes);
// to ascii bytes and back
Charset utf8 = StandardCharsets.UTF_8;
String base85Str = addr.toBase85String();
System.out.println("base 85 string is " + base85Str);
bytes = base85Str.getBytes(utf8);
addr = new IPAddressString(new String(bytes, utf8)).toAddress().toIPv6();
print(addr, bytes);
static void print(IPAddress addr, byte bytes[]) {
System.out.println("got " + addr + " from byte[] len " + bytes.length +
' ' + Arrays.toString(bytes));
}Output:
got 102:304:506:708:90a:b0c:d0e:fff from byte[] len 16 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1]
base 85 string is 0O|s)GT}-*WUn!Z$mO4Z
got 102:304:506:708:90a:b0c:d0e:fff from byte[] len 20 [48, 79, 124, 115, 41, 71, 84, 125, 45, 42, 87, 85, 110, 33, 90, 36, 109, 79, 52, 90]
In this example, subnets are first filtered as IPv4 or IPv6, then IPv4 operations use longs while IPv6 operations use BigInteger to provide the integer values.
boolean inParallel = true;
collectInts(new String[] {"1.2.3.8/29", "2.2.3.8/29", "::/124", "1::/124"},
inParallel);
static Stream<? extends IPAddress> toStream(List<? extends IPAddress> subnetList,
boolean inParallel) {
Stream<? extends IPAddress> joinedStream =
AddressComponentRange.stream(IPAddress::stream, subnetList);
if(inParallel) {
joinedStream = joinedStream.parallel();
}
return joinedStream;
}
static void collectInts(String strs[], boolean inParallel) {
System.out.println("Converting to integers the following " + strs.length +
" subnets: " + Arrays.asList(strs));
List<? extends IPAddress> subnets =
Arrays.stream(strs).
map(IPAddressString::new).
map(IPAddressString::getAddress).
collect(Collectors.toList());
List<Long> ipv4s = toStream(subnets, inParallel).
filter(IPAddress::isIPv4).
map(IPAddress::toIPv4).
mapToLong(IPv4Address::longValue).boxed().
collect(Collectors.toList());
List<BigInteger> ipv6s = toStream(subnets, inParallel).
filter(IPAddress::isIPv6).
map(IPAddress::getValue).
collect(Collectors.toList());
System.out.println("IPv4 addresses as unsigned ints: " + ipv4s);
System.out.println("IPv6 addresses as unsigned ints: " + ipv6s);
}Output:
Converting to integers the following 4 subnets: [1.2.3.8/29, 2.2.3.8/29, ::/124, 1::/124]
IPv4 addresses as unsigned ints: [16909064, 16909065, 16909066, 16909067, 16909068, 16909069, 16909070, 16909071, 33686280, 33686281, 33686282, 33686283, 33686284, 33686285, 33686286, 33686287]
IPv6 addresses as unsigned ints: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5192296858534827628530496329220096, 5192296858534827628530496329220097, 5192296858534827628530496329220098, 5192296858534827628530496329220099, 5192296858534827628530496329220100, 5192296858534827628530496329220101, 5192296858534827628530496329220102, 5192296858534827628530496329220103, 5192296858534827628530496329220104, 5192296858534827628530496329220105, 5192296858534827628530496329220106, 5192296858534827628530496329220107, 5192296858534827628530496329220108, 5192296858534827628530496329220109, 5192296858534827628530496329220110, 5192296858534827628530496329220111]
You can customize how IPv4/v6 conversion takes place, if you are using a tunneling solution or other IPv4/v6 transition mechanism for managing IPv4/IPv6 integration. Such conversion methods generally insert an IPv4 address into the lower 4 bytes of a 16-byte IPv6 address.
Here we extend the IPAddress IPv4 and IPv6 address types. This is not absolutely necessary, you could also simply write routines for conversion using the existing IPv4Address and IPv6Address address types. But extending the type system allows the conversion to be customized with no calls to external conversion routines required, instead using the built-in toIPv4/v6() methods, with the conversion rules governed by the instances themselves, thus reducing the potential for error. This also allows the IPv4 addresses converted from IPv6 to remember details about their IPv6 counterparts when converted back to IPv6.
All address creation within the IPAddress library goes through the network address creator instances. Providing your own network instances allows you to insert your own address types. You override the address creation methods to create your own address instances. In the creator types there are a few methods that create address instances, but for the purposes of this example we need override only one method in each, the one used from most places in the library taking an address section as its only argument.
static IPv4AddressNetwork myIPv4Network = new IPv4AddressNetwork() {
@Override
protected IPv4AddressCreator createAddressCreator() {
return new IPv4AddressCreator(myIPv4Network) {
@Override
public IPv4Address createAddress(
IPv4AddressSection section) {
return new MyIPv4Address(section);
}
};
}
};
static IPv6AddressNetwork myIPv6Network = new IPv6AddressNetwork() {
@Override
protected IPv6AddressCreator createAddressCreator() {
return new IPv6AddressCreator(this) {
@Override
public IPv6Address createAddress(
IPv6AddressSection section) {
return new MyIPv6Address(section);
}
};
}
};When extending the address classes, you can override the getNetwork, getIPv4Network, and getIPv6Network methods to return your own network instances. For the purpose of using our customized IPv4/v6 address conversion, we also override the isIPv4/v6Convertible() and toIPv4/v6() methods and put the conversion code in the toIPv4/v6() methods.
The extension of IPv6Address:
static class MyIPv6Address extends IPv6Address {
public MyIPv6Address(IPv6AddressSection section) {
super(section);
}
public MyIPv6Address(IPv6AddressSegment segments[]) {
super(segments);
}
@Override
public IPv6AddressNetwork getNetwork() {
return myIPv6Network;
}
@Override
public IPv4AddressNetwork getIPv4Network() {
return myIPv4Network;
}
@Override
public boolean isIPv4Convertible() {
return isTeredo() ||
is6To4() ||
is6Over4() ||
isIsatap() ||
isIPv4Translatable() ||
isLoopback() ||
super.isIPv4Convertible();
}
@Override
public IPv4Address toIPv4() {
MyIPv4Address result;
if(isTeredo()) {
result = new MyIPv4Address(
~getEmbeddedIPv4Address().intValue());
result.teredoServer =
(MyIPv4Address) getEmbeddedIPv4Address(4);
result.teredoFlags = getSegment(4).getSegmentValue();
result.teredoPort = ~getSegment(5).getSegmentValue();
result.isTeredo = true;
} else if(is6To4()) {
result = (MyIPv4Address) get6To4IPv4Address();
result.is6To4 = true;
} else if(is6Over4()) {
result = (MyIPv4Address) getEmbeddedIPv4Address();
result.is6Over4 = true;
} else if(isIsatap()) {
result = (MyIPv4Address) getEmbeddedIPv4Address();
result.isIsatap = true;
} else if(isIPv4Translatable()) {
result = (MyIPv4Address) getEmbeddedIPv4Address();
result.isIPv4Translatable = true;
} else if(isLoopback()) {
return getIPv4Network().getLoopback();
} else {
result = (MyIPv4Address) super.toIPv4();
if(result != null) {
result.isIPv4Mapped = true;
}
}
return result;
}
private String typeStr() {
return isTeredo() ? "Teredo " :
is6To4() ? "6 To 4 " :
is6Over4() ? "6 Over 4 " :
isIsatap() ? "Isatap " :
isIPv4Translatable() ? "IPv4-translated " :
isLoopback() ? "loopback " :
isIPv4Mapped() ? "IPv4-mapped " : "";
}
@Override
public String toString() {
return typeStr() + getClass().getSimpleName() + ' ' +
super.toString();
}
}The extension of IPv4Address:
static class MyIPv4Address extends IPv4Address {
boolean isTeredo, is6To4, is6Over4, isIsatap, isIPv4Translatable,
isIPv4Mapped;
int teredoFlags, teredoPort;
MyIPv4Address teredoServer;
public MyIPv4Address(IPv4AddressSection section) {
super(section);
}
public MyIPv4Address(int address) {
super(address);
}
@Override
public IPv4AddressNetwork getNetwork() {
return myIPv4Network;
}
@Override
public IPv6AddressNetwork getIPv6Network() {
return myIPv6Network;
}
@Override
public boolean isIPv6Convertible() {
// we can always convert, it is just a matter of how
return true;
}
@Override
public IPv6Address toIPv6() {
if(isTeredo || is6To4 || is6Over4 || isIsatap ||
isIPv4Translatable) {
return convert();
} else if(isLoopback()) {
return getIPv6Network().getLoopback();
}
return super.toIPv6();
}
private IPv6Address convert() {
IPv6AddressCreator creator = getIPv6Network().getAddressCreator();
IPv6AddressSegment zero = creator.createSegment(0);
IPv6AddressSegment segs[];
if(isTeredo) {
segs = new IPv6AddressSegment[
IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT];
segs[0] = creator.createSegment(0x2001);
segs[1] = zero;
segs[2] = teredoServer.getSegment(0).
join(creator, teredoServer.getSegment(1));
segs[3] = teredoServer.getSegment(2).
join(creator, teredoServer.getSegment(3));
segs[4] = creator.createSegment(teredoFlags);
segs[5] = creator.createSegment(~teredoPort);
IPv4Address embeddedAddress =
new MyIPv4Address(~intValue());
return embeddedAddress.getIPv6Address(segs);
} else if(is6Over4) {
segs = new IPv6AddressSegment[
IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT];
segs[1] = segs[2] = segs[3] = segs[4] = segs[5] = zero;
segs[0] = creator.createSegment(0xfe80);
return getIPv6Address(segs);
} else if(isIsatap) {
segs = new IPv6AddressSegment[
IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT];
segs[1] = segs[2] = segs[3] = segs[4] = zero;
segs[0] = creator.createSegment(0xfe80);
segs[5] = creator.createSegment(0x5efe);
return getIPv6Address(segs);
} else if(isIPv4Translatable) {
segs = new IPv6AddressSegment[
IPv6Address.MIXED_ORIGINAL_SEGMENT_COUNT];
segs[0] = segs[1] = segs[2] = segs[3] = segs[5] = zero;
segs[4] = creator.createSegment(0xffff);
return getIPv6Address(segs);
} else if(is6To4) {
segs = new IPv6AddressSegment[IPv6Address.SEGMENT_COUNT];
segs[0] = creator.createSegment(0x2002);
segs[1] = getSegment(0).join(creator, getSegment(1));
segs[2] = getSegment(2).join(creator, getSegment(3));
segs[3] = segs[4] = segs[5] = segs[6] = segs[7] = zero;
return new MyIPv6Address(segs);
}
return null;
}
@Override
public String toString() {
return getClass().getSimpleName() + ' ' + super.toString();
}
}String parameters allow you to select the network to use for address creation:
static final IPAddressStringParameters params =
new IPAddressStringParameters.Builder().
getIPv6AddressParametersBuilder().
setNetwork(myIPv6Network).getParentBuilder().
getIPv4AddressParametersBuilder().
setNetwork(myIPv4Network).getParentBuilder().
toParams();We extend IPAddressString for our own type that will always use the configured string parameters:
static class MyIPAddressString extends IPAddressString {
public MyIPAddressString(String addr) {
super(addr, params);
}
}Here we show some sample code demonstrating the conversion. We show both the default standard conversion provided by IPAddress that uses IPv4-mapped IPv6 addresses, and we show the custom types using their own conversion.
String addressStrs[] = {
"1.2.3.4",
"::ffff:a:b", // IPv4-mapped
"a:b:c:d:e:f:a:b",
"::1",
"2002:c000:0204::", // 6 to 4 192.0.2.4
"2001:0000:4136:e378:8000:63bf:3fff:fdd2", // Teredo, 192.0.2.45
"fe80::192.0.2.142", // 6 over 4
"::ffff:0:192.0.2.4", // IPv4-translatable
"fe80::0000:5efe:192.0.2.143" // Isatap
};
System.out.println("Using standard IPv4-mapped conversion");
for(String addrStr : addressStrs) {
convert(addrStr, IPAddressString::new);
}
System.out.println("\n\nUsing customized conversion");
for(String addrStr : addressStrs) {
convert(addrStr, MyIPAddressString::new);
}
static void convert(String str, Function<String, IPAddressString> creator) {
IPAddress address = creator.apply(str).getAddress();
if(address.isIPv4()) {
convertAndBack(address, IPAddress::toIPv6, IPAddress::toIPv4);
} else {
convertAndBack(address, IPAddress::toIPv4, IPAddress::toIPv6);
}
}
static void convertAndBack(IPAddress addr, UnaryOperator<IPAddress> converter,
UnaryOperator<IPAddress> converterBack) {
System.out.println("\nstarting with " + addr);
addr = converter.apply(addr);
if(addr != null) {
System.out.println("converted to " + addr);
addr = converterBack.apply(addr);
if(addr != null) {
System.out.println("converted back to " + addr);
} else {
System.out.println("not convertible back");
}
} else {
System.out.println("not convertible");
}
}Output:
Using standard IPv4-mapped conversion
starting with 1.2.3.4
converted to ::ffff:102:304
converted back to 1.2.3.4
starting with ::ffff:a:b
converted to 0.10.0.11
converted back to ::ffff:a:b
starting with a:b:c:d:e:f:a:b
not convertible
starting with ::1
not convertible
starting with 2002:c000:204::
not convertible
starting with 2001:0:4136:e378:8000:63bf:3fff:fdd2
not convertible
starting with fe80::c000:28e
not convertible
starting with ::ffff:0:c000:204
not convertible
starting with fe80::5efe:c000:28f
not convertible
Using customized conversion
starting with MyIPv4Address 1.2.3.4
converted to IPv4-mapped MyIPv6Address ::ffff:102:304
converted back to MyIPv4Address 1.2.3.4
starting with IPv4-mapped MyIPv6Address ::ffff:a:b
converted to MyIPv4Address 0.10.0.11
converted back to IPv4-mapped MyIPv6Address ::ffff:a:b
starting with MyIPv6Address a:b:c:d:e:f:a:b
not convertible
starting with loopback MyIPv6Address ::1
converted to MyIPv4Address 127.0.0.1
converted back to loopback MyIPv6Address ::1
starting with 6 To 4 MyIPv6Address 2002:c000:204::
converted to MyIPv4Address 192.0.2.4
converted back to 6 To 4 MyIPv6Address 2002:c000:204::
starting with Teredo MyIPv6Address 2001:0:4136:e378:8000:63bf:3fff:fdd2
converted to MyIPv4Address 192.0.2.45
converted back to Teredo MyIPv6Address 2001:0:4136:e378:8000:63bf:3fff:fdd2
starting with 6 Over 4 MyIPv6Address fe80::c000:28e
converted to MyIPv4Address 192.0.2.142
converted back to 6 Over 4 MyIPv6Address fe80::c000:28e
starting with IPv4-translated MyIPv6Address ::ffff:0:c000:204
converted to MyIPv4Address 192.0.2.4
converted back to IPv4-translated MyIPv6Address ::ffff:0:c000:204
starting with Isatap MyIPv6Address fe80::5efe:c000:28f
converted to MyIPv4Address 192.0.2.143
converted back to Isatap MyIPv6Address fe80::5efe:c000:28f
In the output, you can see the standard conversion co-existing with the customized conversion, since it is governed by the address types, starting with either IPAddressString or MyIPAddressString. With the customized conversion, the IPv4 addresses "remember" the conversion to use to go back to their IPv6 counterparts.
Addresses transmitted across a network as multi-byte integers, such as in the header of an IPv4 or IPv6 packet, are in network byte order (big endian), the IP protocol standard ordering of bytes for multi-byte integers.
IPAddress uses the same ordering for segments, byte arrays and integers, much like java.net.InetAddress.
When using a native integer on a little-endian machine, or any other address value in little-endian byte order, you can switch the byte order before writing, or after reading. Most architectures in widespread use today use little-endian, whether Intel/AMD (MacOs or Windows), ARM (supports both, but most devices using ARM are little-endian), IBM Power (was big-endian but now little), so doing the byte reversal between network and native architecture is common.
The example separates IPv4 from IPv6, writes each set to a byte buffer in native byte order, then reads the addresses back in again from each buffer.
IPAddressString ipAddrStrings[] = getAddressStrings(
"1.2.3.4",
"1:2:3:4:5:6:7:8",
"127.0.0.1",
"::1");
ByteBuffer ipv4Bytes = writeToBuffer(filterIPv4(ipAddrStrings));
ByteBuffer ipv6Bytes = writeToBuffer(filterIPv6(ipAddrStrings));
IPAddress ipv4Addresses[] = readFromBuffer(getBufferBytes(ipv4Bytes),
IPv4Address.BYTE_COUNT);
IPAddress ipv6Addresses[] = readFromBuffer(getBufferBytes(ipv6Bytes),
IPv6Address.BYTE_COUNT);
System.out.println("IPv4: " + Arrays.asList(ipv4Addresses));
System.out.println("IPv6: " + Arrays.asList(ipv6Addresses));
static IPAddressString[] getAddressStrings(String ...addrStr) {
return Arrays.stream(addrStr).
map(IPAddressString::new).
toArray(IPAddressString[]::new);
}
static IPv4Address[] filterIPv4(IPAddressString ipAddrStrs[]) {
return Arrays.stream(ipAddrStrs).
filter(IPAddressString::isIPv4).
map(ipAddrStr -> ipAddrStr.getAddress().toIPv4()).
toArray(IPv4Address[]::new);
}
static IPv6Address[] filterIPv6(IPAddressString ipAddrStrs[]) {
return Arrays.stream(ipAddrStrs).
filter(IPAddressString::isIPv6).
map(ipAddrStr -> ipAddrStr.getAddress().toIPv6()).
toArray(IPv6Address[]::new);
}
static ByteBuffer writeToBuffer(IPv4Address ipv4Addresses[]) {
ByteBuffer buf = ByteBuffer.allocateDirect(
ipv4Addresses.length * IPv4Address.BYTE_COUNT);
buf.order(ByteOrder.nativeOrder()); // use correct order when writing ints
for(IPv4Address addr : ipv4Addresses) {
buf.putInt(addr.intValue());
}
return buf;
}
static ByteBuffer writeToBuffer(IPv6Address ipv6Addresses[]) {
int addressByteSize = IPv6Address.BYTE_COUNT;
ByteBuffer buf = ByteBuffer.allocateDirect(
ipv6Addresses.length * addressByteSize);
boolean isLittleEndian =
ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
byte bytes[] = new byte[addressByteSize];
for(IPAddress addr : ipv6Addresses) {
if(isLittleEndian) {
addr = addr.reverseBytes();
}
addr.getBytes(bytes);
buf.put(bytes);
}
return buf;
}
static IPAddress[] readFromBuffer(byte bytes[], int addressByteCount) {
IPAddressGenerator generator = new IPAddressGenerator();
IPAddress result[] = new IPAddress[bytes.length / addressByteCount];
boolean isLittleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
for(int i = 0, j = i + addressByteCount, k = 0; i < bytes.length;
i = j, j += addressByteCount, k++) {
IPAddress addr = generator.from(bytes, i, j);
if(isLittleEndian) {
addr = addr.reverseBytes();
}
result[k] = addr;
}
return result;
}
static byte[] getBufferBytes(ByteBuffer buffer) {
buffer.rewind();
byte bytes[];
if(buffer.hasArray()) {
bytes = buffer.array();
} else {
bytes = new byte[buffer.remaining()];
buffer.get(bytes);
}
return bytes;
}The output shows the addresses read back from the two buffers, which matches the original four addresses.
Output:
IPv4: [1.2.3.4, 127.0.0.1]
IPv6: [1:2:3:4:5:6:7:8, ::1]