Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ length).
+
Parameters:
first - first ip address of the range
last - last ip address of the range
prefixLength - prefix length with which to allocate subnets from this range
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+
Constant Field Values
+
The <a href="constant-values.html">Constant Field Values</a> page lists the static final fields and their values.
+
+
+This help file applies to API documentation generated using the standard doclet.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
public static IPv6Address fromLongs(long highBits,
+ long lowBits)
+
Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ IPv6Addresses from a String or an InetAddress. The internal representation of an IPv6Address is exactly
+ these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
public static IPv6Address fromLongs(long highBits,
+ long lowBits)
+
Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ IPv6Addresses from a String or an InetAddress. The internal representation of an IPv6Address is exactly
+ these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ and the remaining 32 bits are the IPv4 address.
+
Returns:
true if the address is an IPv4-mapped IPv6 addresses.
Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ addresses whenever possible.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
public static IPv6Address fromLongs(long highBits,
+ long lowBits)
+
Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ IPv6Addresses from a String or an InetAddress. The internal representation of an IPv6Address is exactly
+ these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ and the remaining 32 bits are the IPv4 address.
+
Returns:
true if the address is an IPv4-mapped IPv6 addresses.
Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ addresses whenever possible.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
public static IPv6Address fromLongs(long highBits,
+ long lowBits)
+
Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ IPv6Addresses from a String or an InetAddress. The internal representation of an IPv6Address is exactly
+ these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
Create an IPv6 address from a (positive) BigInteger. The magnitude of the BigInteger represents
+ the IPv6 address value. Or in other words, the BigInteger with value N defines the Nth possible IPv6 address.
BigInteger representation. The magnitude of the BigInteger represents the IPv6 address
+ value. Or in other words, the BigInteger with value N defines the Nth possible IPv6 address.
Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ and the remaining 32 bits are the IPv4 address.
+
Returns:
true if the address is an IPv4-mapped IPv6 addresses.
Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ addresses whenever possible.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
public static IPv6Address fromLongs(long highBits,
+ long lowBits)
+
Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ IPv6Addresses from a String or an InetAddress. The internal representation of an IPv6Address is exactly
+ these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
Create an IPv6 address from a (positive) BigInteger. The magnitude of the BigInteger represents
+ the IPv6 address value. Or in other words, the BigInteger with value N defines the Nth possible IPv6 address.
BigInteger representation. The magnitude of the BigInteger represents the IPv6 address
+ value. Or in other words, the BigInteger with value N defines the Nth possible IPv6 address.
Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ and the remaining 32 bits are the IPv4 address.
+
Returns:
true if the address is an IPv4-mapped IPv6 addresses.
Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ addresses whenever possible.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Deaggregate a range of IPv6 addresses (which is not necessarily aligned with a single IPv6 network) into a minimal set of non
+ overlapping consecutive subnets.
Deaggregate a range of IPv6 addresses (which is not necessarily aligned with a single IPv6 network) into a minimal set of non
+ overlapping consecutive subnets.
+
Returns:
iterator of IPv6 networks that all together define the minimal set of subnets by which the range can be represented.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Deaggregate a range of IPv6 addresses (which is not necessarily aligned with a single IPv6 network) into a minimal set of non
+ overlapping consecutive subnets.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Deaggregate a range of IPv6 addresses (which is not necessarily aligned with a single IPv6 network) into a minimal set of non
+ overlapping consecutive subnets.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ length).
+
Parameters:
first - first ip address of the range
last - last ip address of the range
prefixLength - prefix length with which to allocate subnets from this range
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+
Constant Field Values
+
The <a href="constant-values.html">Constant Field Values</a> page lists the static final fields and their values.
+
+
+This help file applies to API documentation generated using the standard doclet.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ length).
+
Parameters:
first - first ip address of the range
last - last ip address of the range
prefixLength - prefix length with which to allocate subnets from this range
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+
Constant Field Values
+
The <a href="constant-values.html">Constant Field Values</a> page lists the static final fields and their values.
+
+
+This help file applies to API documentation generated using the standard doclet.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ length).
+
Parameters:
first - first ip address of the range
last - last ip address of the range
prefixLength - prefix length with which to allocate subnets from this range
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+
Constant Field Values
+
The <a href="constant-values.html">Constant Field Values</a> page lists the static final fields and their values.
+
+
+This help file applies to API documentation generated using the standard doclet.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool in between the given first and last address (inclusive) which is completely free. The given subnet size is the network
+ mask (thus size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of
+ this size (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet
+ size).
+
Parameters:
first - first ip address of the range
last - last ip address of the range
allocationSubnetSize - size of the subnets that will be allocated
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+
Constant Field Values
+
The <a href="constant-values.html">Constant Field Values</a> page lists the static final fields and their values.
+
+
+This help file applies to API documentation generated using the standard doclet.
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+
+
Constant Field Values
+
The <a href="constant-values.html">Constant Field Values</a> page lists the static final fields and their values.
+
+
+This help file applies to API documentation generated using the standard doclet.
+This class contains some helpers for working with BitSets. These are generally not necessary in JDK7, since the BitSet.valueOf(long[])
+ method. However, for java-6 compatibility, we go this way.
+
+Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
+
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
+
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
+
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
+
+Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
+
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
+
+
Parameters:
one - address one
two - address two, should be bigger than address one
+Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+
+This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+Package
+
+
+
+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:
+
Interfaces (italic)
Classes
Enums
Exceptions
Errors
Annotation Types
+
+
+Class/Interface
+
+
+
+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
Class inheritance diagram
Direct Subclasses
All Known Subinterfaces
All Known Implementing Classes
Class/interface declaration
Class/interface description
+
+
Nested Class Summary
Field Summary
Constructor Summary
Method Summary
+
+
Field Detail
Constructor Detail
Method Detail
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+Annotation Type
+
+
+
+Each annotation type has its own separate page with the following sections:
+
Annotation Type declaration
Annotation Type description
Required Element Summary
Optional Element Summary
Element Detail
+
+
+
+Enum
+
+
+
+Each enum has its own separate page with the following sections:
+
Enum declaration
Enum description
Enum Constant Summary
Enum Constant Detail
+
+
+Use
+
+Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+Tree (Class Hierarchy)
+
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+Deprecated API
+
+The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+Index
+
+The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+Prev/Next
+These links take you to the next or previous class, interface, package, or related page.
+Frames/No Frames
+These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+Serialized Form
+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
+Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
+
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
+
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
+Immutable representation of a continuous range of IPv6 addresses (bounds included).
+
+
+
+
+
Author:
+
Jan Van Besien
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+ int
+
compareTo(IPv6AddressRange that)
+
+
+ The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
+
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
+
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
+
+Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
+
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
+
+
Parameters:
one - address one
two - address two, should be bigger than address one
+Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+
IPv6AddressRange.compareTo(IPv6AddressRange that)
+
+
+ The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+Package
+
+
+
+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:
+
Interfaces (italic)
Classes
Enums
Exceptions
Errors
Annotation Types
+
+
+Class/Interface
+
+
+
+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
Class inheritance diagram
Direct Subclasses
All Known Subinterfaces
All Known Implementing Classes
Class/interface declaration
Class/interface description
+
+
Nested Class Summary
Field Summary
Constructor Summary
Method Summary
+
+
Field Detail
Constructor Detail
Method Detail
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+Annotation Type
+
+
+
+Each annotation type has its own separate page with the following sections:
+
Annotation Type declaration
Annotation Type description
Required Element Summary
Optional Element Summary
Element Detail
+
+
+
+Enum
+
+
+
+Each enum has its own separate page with the following sections:
+
Enum declaration
Enum description
Enum Constant Summary
Enum Constant Detail
+
+
+Use
+
+Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+Tree (Class Hierarchy)
+
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+Deprecated API
+
+The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+Index
+
+The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+Prev/Next
+These links take you to the next or previous class, interface, package, or related page.
+Frames/No Frames
+These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+Serialized Form
+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
public final class IPv6AddressPool
+extends Object
+
Immutable representation of an IPv6 address pool.
+
+ An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ range.
Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+
Parameters:
range - range from within to allocate
allocationSubnetSize - size of the subnets that will be allocated
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ single address). If an address somewhere else in the range is removed, two new ranges are returned.
Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ range is removed, two new ranges are returned.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+ Note that the natural order does thus not compare sizes of ranges.
Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ not all ranges are valid networks).
Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ length") which contains both addresses.
+
Parameters:
one - address one
two - address two, should be bigger than address one
public final class IPv6NetworkMask
+extends Object
+
Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+
+
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+
+
Interfaces (italic)
+
Classes
+
Enums
+
Exceptions
+
Errors
+
Annotation Types
+
+
+
+
Class/Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+
+
Class inheritance diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class/interface declaration
+
Class/interface description
+
+
+
Nested Class Summary
+
Field Summary
+
Constructor Summary
+
Method Summary
+
+
+
Field Detail
+
Constructor Detail
+
Method Detail
+
+
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Annotation Type
+
Each annotation type has its own separate page with the following sections:
+
+
Annotation Type declaration
+
Annotation Type description
+
Required Element Summary
+
Optional Element Summary
+
Element Detail
+
+
+
+
Enum
+
Each enum has its own separate page with the following sections:
+
+
Enum declaration
+
Enum description
+
Enum Constant Summary
+
Enum Constant Detail
+
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+
+
+
Deprecated API
+
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+
+
+
Index
+
The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+
+
+
Prev/Next
+
These links take you to the next or previous class, interface, package, or related page.
+
+
+
Frames/No Frames
+
These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
All Classes
+
The All Classes link shows all classes and interfaces except non-static nested types.
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
The natural order of IPv6AddressRanges orders them on increasing first addresses, and on increasing last
+ address if the first address would be equal.
+
+
diff --git a/artifacts/0.9/doc/apidocs/package-list b/artifacts/0.9/doc/apidocs/package-list
new file mode 100644
index 0000000..30c4618
--- /dev/null
+++ b/artifacts/0.9/doc/apidocs/package-list
@@ -0,0 +1 @@
+com.googlecode.ipv6
diff --git a/artifacts/0.9/doc/apidocs/resources/background.gif b/artifacts/0.9/doc/apidocs/resources/background.gif
new file mode 100644
index 0000000..f471940
Binary files /dev/null and b/artifacts/0.9/doc/apidocs/resources/background.gif differ
diff --git a/artifacts/0.9/doc/apidocs/resources/tab.gif b/artifacts/0.9/doc/apidocs/resources/tab.gif
new file mode 100644
index 0000000..1a73a83
Binary files /dev/null and b/artifacts/0.9/doc/apidocs/resources/tab.gif differ
diff --git a/artifacts/0.9/doc/apidocs/resources/titlebar.gif b/artifacts/0.9/doc/apidocs/resources/titlebar.gif
new file mode 100644
index 0000000..17443b3
Binary files /dev/null and b/artifacts/0.9/doc/apidocs/resources/titlebar.gif differ
diff --git a/artifacts/0.9/doc/apidocs/resources/titlebar_end.gif b/artifacts/0.9/doc/apidocs/resources/titlebar_end.gif
new file mode 100644
index 0000000..3ad78d4
Binary files /dev/null and b/artifacts/0.9/doc/apidocs/resources/titlebar_end.gif differ
diff --git a/artifacts/0.9/doc/apidocs/stylesheet.css b/artifacts/0.9/doc/apidocs/stylesheet.css
new file mode 100644
index 0000000..0aeaa97
--- /dev/null
+++ b/artifacts/0.9/doc/apidocs/stylesheet.css
@@ -0,0 +1,474 @@
+/* Javadoc style sheet */
+/*
+Overall document style
+*/
+body {
+ background-color:#ffffff;
+ color:#353833;
+ font-family:Arial, Helvetica, sans-serif;
+ font-size:76%;
+ margin:0;
+}
+a:link, a:visited {
+ text-decoration:none;
+ color:#4c6b87;
+}
+a:hover, a:focus {
+ text-decoration:none;
+ color:#bb7a2a;
+}
+a:active {
+ text-decoration:none;
+ color:#4c6b87;
+}
+a[name] {
+ color:#353833;
+}
+a[name]:hover {
+ text-decoration:none;
+ color:#353833;
+}
+pre {
+ font-size:1.3em;
+}
+h1 {
+ font-size:1.8em;
+}
+h2 {
+ font-size:1.5em;
+}
+h3 {
+ font-size:1.4em;
+}
+h4 {
+ font-size:1.3em;
+}
+h5 {
+ font-size:1.2em;
+}
+h6 {
+ font-size:1.1em;
+}
+ul {
+ list-style-type:disc;
+}
+code, tt {
+ font-size:1.2em;
+}
+dt code {
+ font-size:1.2em;
+}
+table tr td dt code {
+ font-size:1.2em;
+ vertical-align:top;
+}
+sup {
+ font-size:.6em;
+}
+/*
+Document title and Copyright styles
+*/
+.clear {
+ clear:both;
+ height:0px;
+ overflow:hidden;
+}
+.aboutLanguage {
+ float:right;
+ padding:0px 21px;
+ font-size:.8em;
+ z-index:200;
+ margin-top:-7px;
+}
+.legalCopy {
+ margin-left:.5em;
+}
+.bar a, .bar a:link, .bar a:visited, .bar a:active {
+ color:#FFFFFF;
+ text-decoration:none;
+}
+.bar a:hover, .bar a:focus {
+ color:#bb7a2a;
+}
+.tab {
+ background-color:#0066FF;
+ background-image:url(resources/titlebar.gif);
+ background-position:left top;
+ background-repeat:no-repeat;
+ color:#ffffff;
+ padding:8px;
+ width:5em;
+ font-weight:bold;
+}
+/*
+Navigation bar styles
+*/
+.bar {
+ background-image:url(resources/background.gif);
+ background-repeat:repeat-x;
+ color:#FFFFFF;
+ padding:.8em .5em .4em .8em;
+ height:auto;/*height:1.8em;*/
+ font-size:1em;
+ margin:0;
+}
+.topNav {
+ background-image:url(resources/background.gif);
+ background-repeat:repeat-x;
+ color:#FFFFFF;
+ float:left;
+ padding:0;
+ width:100%;
+ clear:right;
+ height:2.8em;
+ padding-top:10px;
+ overflow:hidden;
+}
+.bottomNav {
+ margin-top:10px;
+ background-image:url(resources/background.gif);
+ background-repeat:repeat-x;
+ color:#FFFFFF;
+ float:left;
+ padding:0;
+ width:100%;
+ clear:right;
+ height:2.8em;
+ padding-top:10px;
+ overflow:hidden;
+}
+.subNav {
+ background-color:#dee3e9;
+ border-bottom:1px solid #9eadc0;
+ float:left;
+ width:100%;
+ overflow:hidden;
+}
+.subNav div {
+ clear:left;
+ float:left;
+ padding:0 0 5px 6px;
+}
+ul.navList, ul.subNavList {
+ float:left;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.navList li{
+ list-style:none;
+ float:left;
+ padding:3px 6px;
+}
+ul.subNavList li{
+ list-style:none;
+ float:left;
+ font-size:90%;
+}
+.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {
+ color:#FFFFFF;
+ text-decoration:none;
+}
+.topNav a:hover, .bottomNav a:hover {
+ text-decoration:none;
+ color:#bb7a2a;
+}
+.navBarCell1Rev {
+ background-image:url(resources/tab.gif);
+ background-color:#a88834;
+ color:#FFFFFF;
+ margin: auto 5px;
+ border:1px solid #c9aa44;
+}
+/*
+Page header and footer styles
+*/
+.header, .footer {
+ clear:both;
+ margin:0 20px;
+ padding:5px 0 0 0;
+}
+.indexHeader {
+ margin:10px;
+ position:relative;
+}
+.indexHeader h1 {
+ font-size:1.3em;
+}
+.title {
+ color:#2c4557;
+ margin:10px 0;
+}
+.subTitle {
+ margin:5px 0 0 0;
+}
+.header ul {
+ margin:0 0 25px 0;
+ padding:0;
+}
+.footer ul {
+ margin:20px 0 5px 0;
+}
+.header ul li, .footer ul li {
+ list-style:none;
+ font-size:1.2em;
+}
+/*
+Heading styles
+*/
+div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
+ background-color:#dee3e9;
+ border-top:1px solid #9eadc0;
+ border-bottom:1px solid #9eadc0;
+ margin:0 0 6px -8px;
+ padding:2px 5px;
+}
+ul.blockList ul.blockList ul.blockList li.blockList h3 {
+ background-color:#dee3e9;
+ border-top:1px solid #9eadc0;
+ border-bottom:1px solid #9eadc0;
+ margin:0 0 6px -8px;
+ padding:2px 5px;
+}
+ul.blockList ul.blockList li.blockList h3 {
+ padding:0;
+ margin:15px 0;
+}
+ul.blockList li.blockList h2 {
+ padding:0px 0 20px 0;
+}
+/*
+Page layout container styles
+*/
+.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {
+ clear:both;
+ padding:10px 20px;
+ position:relative;
+}
+.indexContainer {
+ margin:10px;
+ position:relative;
+ font-size:1.0em;
+}
+.indexContainer h2 {
+ font-size:1.1em;
+ padding:0 0 3px 0;
+}
+.indexContainer ul {
+ margin:0;
+ padding:0;
+}
+.indexContainer ul li {
+ list-style:none;
+}
+.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {
+ font-size:1.1em;
+ font-weight:bold;
+ margin:10px 0 0 0;
+ color:#4E4E4E;
+}
+.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {
+ margin:10px 0 10px 20px;
+}
+.serializedFormContainer dl.nameValue dt {
+ margin-left:1px;
+ font-size:1.1em;
+ display:inline;
+ font-weight:bold;
+}
+.serializedFormContainer dl.nameValue dd {
+ margin:0 0 0 1px;
+ font-size:1.1em;
+ display:inline;
+}
+/*
+List styles
+*/
+ul.horizontal li {
+ display:inline;
+ font-size:0.9em;
+}
+ul.inheritance {
+ margin:0;
+ padding:0;
+}
+ul.inheritance li {
+ display:inline;
+ list-style:none;
+}
+ul.inheritance li ul.inheritance {
+ margin-left:15px;
+ padding-left:15px;
+ padding-top:1px;
+}
+ul.blockList, ul.blockListLast {
+ margin:10px 0 10px 0;
+ padding:0;
+}
+ul.blockList li.blockList, ul.blockListLast li.blockList {
+ list-style:none;
+ margin-bottom:25px;
+}
+ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {
+ padding:0px 20px 5px 10px;
+ border:1px solid #9eadc0;
+ background-color:#f9f9f9;
+}
+ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {
+ padding:0 0 5px 8px;
+ background-color:#ffffff;
+ border:1px solid #9eadc0;
+ border-top:none;
+}
+ul.blockList ul.blockList ul.blockList ul.blockList li.blockList {
+ margin-left:0;
+ padding-left:0;
+ padding-bottom:15px;
+ border:none;
+ border-bottom:1px solid #9eadc0;
+}
+ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
+ list-style:none;
+ border-bottom:none;
+ padding-bottom:0;
+}
+table tr td dl, table tr td dl dt, table tr td dl dd {
+ margin-top:0;
+ margin-bottom:1px;
+}
+/*
+Table styles
+*/
+.contentContainer table, .classUseContainer table, .constantValuesContainer table {
+ border-bottom:1px solid #9eadc0;
+ width:100%;
+}
+.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table {
+ width:100%;
+}
+.contentContainer .description table, .contentContainer .details table {
+ border-bottom:none;
+}
+.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{
+ vertical-align:top;
+ padding-right:20px;
+}
+.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast,
+.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast,
+.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne,
+.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne {
+ padding-right:3px;
+}
+.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption {
+ position:relative;
+ text-align:left;
+ background-repeat:no-repeat;
+ color:#FFFFFF;
+ font-weight:bold;
+ clear:none;
+ overflow:hidden;
+ padding:0px;
+ margin:0px;
+}
+caption a:link, caption a:hover, caption a:active, caption a:visited {
+ color:#FFFFFF;
+}
+.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span {
+ white-space:nowrap;
+ padding-top:8px;
+ padding-left:8px;
+ display:block;
+ float:left;
+ background-image:url(resources/titlebar.gif);
+ height:18px;
+}
+.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd {
+ width:10px;
+ background-image:url(resources/titlebar_end.gif);
+ background-repeat:no-repeat;
+ background-position:top right;
+ position:relative;
+ float:left;
+}
+ul.blockList ul.blockList li.blockList table {
+ margin:0 0 12px 0px;
+ width:100%;
+}
+.tableSubHeadingColor {
+ background-color: #EEEEFF;
+}
+.altColor {
+ background-color:#eeeeef;
+}
+.rowColor {
+ background-color:#ffffff;
+}
+.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td {
+ text-align:left;
+ padding:3px 3px 3px 7px;
+}
+th.colFirst, th.colLast, th.colOne, .constantValuesContainer th {
+ background:#dee3e9;
+ border-top:1px solid #9eadc0;
+ border-bottom:1px solid #9eadc0;
+ text-align:left;
+ padding:3px 3px 3px 7px;
+}
+td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {
+ font-weight:bold;
+}
+td.colFirst, th.colFirst {
+ border-left:1px solid #9eadc0;
+ white-space:nowrap;
+}
+td.colLast, th.colLast {
+ border-right:1px solid #9eadc0;
+}
+td.colOne, th.colOne {
+ border-right:1px solid #9eadc0;
+ border-left:1px solid #9eadc0;
+}
+table.overviewSummary {
+ padding:0px;
+ margin-left:0px;
+}
+table.overviewSummary td.colFirst, table.overviewSummary th.colFirst,
+table.overviewSummary td.colOne, table.overviewSummary th.colOne {
+ width:25%;
+ vertical-align:middle;
+}
+table.packageSummary td.colFirst, table.overviewSummary th.colFirst {
+ width:25%;
+ vertical-align:middle;
+}
+/*
+Content styles
+*/
+.description pre {
+ margin-top:0;
+}
+.deprecatedContent {
+ margin:0;
+ padding:10px 0;
+}
+.docSummary {
+ padding:0;
+}
+/*
+Formatting effect styles
+*/
+.sourceLineNo {
+ color:green;
+ padding:0 30px 0 0;
+}
+h1.hidden {
+ visibility:hidden;
+ overflow:hidden;
+ font-size:.9em;
+}
+.block {
+ display:block;
+ margin:3px 0 0 0;
+}
+.strong {
+ font-weight:bold;
+}
diff --git a/release.txt b/release.txt
new file mode 100644
index 0000000..4dfe61a
--- /dev/null
+++ b/release.txt
@@ -0,0 +1,12 @@
+HOWTO make a release:
+
+- mvn release:prepare in trunk (will ask for gpg passphrase)
+- mvn release:perform in trunk
+- step 8 in https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide
+- mkdir -p artifacts//doc/
+- cp -r trunk/target/checkout/target/apidocs artifacts//doc/
+- svn add artifacts/
+- find -name '*.html' -exec svn propset svn:mime-type text/html {} \;
+- svn commit
+- update documentation
+- provide new download on google code
diff --git a/tags/ipv6-0.1/pom.xml b/tags/ipv6-0.1/pom.xml
new file mode 100644
index 0000000..e9d2b86
--- /dev/null
+++ b/tags/ipv6-0.1/pom.xml
@@ -0,0 +1,41 @@
+
+ 4.0.0
+ be.jvb.ipv6
+ ipv6
+ 0.1
+ jar
+ IPv6
+
+ https://java-ipv6.googlecode.com/svn/tags/ipv6-0.1
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/ipv6-0.1
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.7
+
+
+
+
+
+
diff --git a/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6Address.java b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6Address.java
new file mode 100644
index 0000000..9c062a6
--- /dev/null
+++ b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6Address.java
@@ -0,0 +1,299 @@
+package be.jvb.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String longNotation = IPv6AddressHelpers.expandShortNotation(string);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return IPv6AddressHelpers.mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return IPv6AddressHelpers.parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(long value)
+ {
+ final long newLowBits = lowBits + value;
+ if ((lowBits < 0 && newLowBits >= 0) || (lowBits >= 0 && newLowBits < 0))
+ {
+ // sign changes indicate carry out -> carry out from low bits into high bits
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(long value)
+ {
+ final long newLowBits = lowBits - value;
+ if ((lowBits < 0 && newLowBits >= 0) || (lowBits >= 0 && newLowBits < 0))
+ {
+ // sign changes indicate carry out -> carry out from high bits into low bits
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+
+ /**
+ * Mask the address with the given prefix length.
+ *
+ * @param prefixLength prefix length
+ * @return an address of which the last 128 - prefixLength bits are zero
+ */
+ public IPv6Address maskWithPrefixLength(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ if (prefixLength == 128)
+ {
+ return this;
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (prefixLength > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - prefixLength)), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given prefix length.
+ *
+ * @param prefixLength prefix length
+ * @return an address of which the last 128 - prefixLength bits are one
+ */
+ public IPv6Address maximumAddressWithPrefixLength(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ if (prefixLength == 128)
+ {
+ return this;
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> prefixLength), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ final String[] strings = toStringArray();
+
+ final StringBuilder result = new StringBuilder();
+
+ boolean shortHandNotationUsed = false;
+ boolean shortHandNotationBusy = false;
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (!shortHandNotationUsed && i < N_SHORTS - 1 && IPv6AddressHelpers.isZeroString(strings[i]) && IPv6AddressHelpers
+ .isZeroString(strings[i + 1]))
+ {
+ shortHandNotationUsed = true;
+ shortHandNotationBusy = true;
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(IPv6AddressHelpers.isZeroString(strings[i]) && shortHandNotationBusy))
+ {
+ shortHandNotationBusy = false;
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toStringArray()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%X", shorts[i]);
+ }
+ return strings;
+ }
+
+ public short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ? Long.numberOfTrailingZeros(highBits) + 64 : Long.numberOfTrailingZeros(lowBits);
+ }
+
+ int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+}
diff --git a/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..40e229f
--- /dev/null
+++ b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,110 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isZeroString(String string)
+ {
+ return "0".equals(string);
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressPool.java b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..3291219
--- /dev/null
+++ b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
@@ -0,0 +1,303 @@
+package be.jvb.ipv6;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool extends IPv6AddressRange
+{
+ private final SortedSet freeRanges;
+
+ private final int prefixLength;
+
+ /**
+ * Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ * length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ * length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ * length).
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ */
+ public IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength)
+ {
+ // in the beginning, all is free
+ this(first, last, prefixLength, new TreeSet(Arrays.asList(new IPv6AddressRange(first, last))));
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges.
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength,
+ final SortedSet freeRanges)
+ {
+ super(first, last);
+
+ this.prefixLength = prefixLength;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+
+ validateFreeRanges(first, last, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(first, last, prefixLength);
+ }
+
+ private void validateFreeRanges(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(first, last, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be usefull. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(first) >= 0 && toValidate.last().getLast().compareTo(last) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(IPv6Address first, IPv6Address last, int prefixLength)
+ {
+ final int allocatableBits = 128 - prefixLength;
+
+ if (first.numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (last.numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = new IPv6Network(firstFreeRange.getFirst(), prefixLength);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (toAllocate.getPrefixLength() != this.prefixLength)
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /" + prefixLength);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges);
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (network.getPrefixLength() != prefixLength)
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getPrefixLength() + "] can not be free in a pool which uses prefix length [" +
+ prefixLength + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+}
diff --git a/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressRange.java b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..3ba031f
--- /dev/null
+++ b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
@@ -0,0 +1,194 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ public IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(new IPv6AddressRange(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(new IPv6AddressRange(first, last.subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, address.subtract(1)),
+ new IPv6AddressRange(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return new IPv6AddressRange(address, last);
+ else if (address.compareTo(last) > 0)
+ return new IPv6AddressRange(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(new IPv6AddressRange(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(new IPv6AddressRange(first, network.getLast().subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, network.getFirst().subtract(1)),
+ new IPv6AddressRange(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (this.first != that.first)
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ return current = current.add(1);
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6Network.java b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6Network.java
new file mode 100644
index 0000000..479c68d
--- /dev/null
+++ b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6Network.java
@@ -0,0 +1,114 @@
+package be.jvb.ipv6;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final int prefixLength;
+
+ /**
+ * Construct from address and prefix length.
+ *
+ * @param address address
+ * @param prefixLength prefix length, in range ]0, 128]
+ */
+ public IPv6Network(IPv6Address address, int prefixLength)
+ {
+ super(address.maskWithPrefixLength(prefixLength), address.maximumAddressWithPrefixLength(prefixLength));
+
+ this.address = address.maskWithPrefixLength(prefixLength);
+ this.prefixLength = prefixLength;
+ }
+
+ /**
+ * Construct from first and last address. This will construct the smallest possible network ("longest prefix length") which contains
+ * both addresses.
+ *
+ * @param first first address
+ * @param last last address
+ */
+ public IPv6Network(IPv6Address first, IPv6Address last)
+ {
+ super(first.maskWithPrefixLength(IPv6NetworkHelpers.longestPrefixLength(first, last)),
+ first.maximumAddressWithPrefixLength(IPv6NetworkHelpers.longestPrefixLength(first, last)));
+
+ this.prefixLength = IPv6NetworkHelpers.longestPrefixLength(first, last);
+ this.address = this.getFirst();
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return new IPv6Network(networkAddress, prefixLength);
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + prefixLength;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (prefixLength != that.prefixLength) return false;
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = address != null ? address.hashCode() : 0;
+ result = 31 * result + prefixLength;
+ return result;
+ }
+
+ public int getPrefixLength()
+ {
+ return prefixLength;
+ }
+}
diff --git a/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..ca4259d
--- /dev/null
+++ b/tags/ipv6-0.1/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,30 @@
+package be.jvb.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = BitSet.valueOf(new long[]{first.getLowBits(), first.getHighBits()});
+ final BitSet lastBits = BitSet.valueOf(new long[]{last.getLowBits(), last.getHighBits()});
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet firstBits, BitSet lastBits)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (firstBits.get(i) == lastBits.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/Examples.java b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/Examples.java
new file mode 100644
index 0000000..686c2d4
--- /dev/null
+++ b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/Examples.java
@@ -0,0 +1,53 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+/**
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = new IPv6AddressRange(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6Network range = new IPv6Network(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"), 120);
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..41abad5
--- /dev/null
+++ b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,171 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ new IPv6AddressPool(fromString("2001::1"), fromString("2001::ffff:ffff"), 120);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:fffe"), 120);
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::ab00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::abcd:ef00"), fromString("2001::abcd:efff"), 120);
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(new IPv6Network(fromString("::1"), 128));
+
+ assertTrue(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("::1"), 128));
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate(new IPv6Network(fromString("::1"), 128)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(new IPv6Network(fromString("::99"), 128));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(new IPv6Network(fromString("::" + i), 128));
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::200"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+}
diff --git a/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..7c5d055
--- /dev/null
+++ b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,90 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ new IPv6AddressRange(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8")).size());
+ assertEquals(0, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4")).size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+}
diff --git a/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressTest.java b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..eb4594b
--- /dev/null
+++ b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
@@ -0,0 +1,221 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromInetAddress;
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Long.MAX_VALUE), fromString("::").add(Long.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Long.MAX_VALUE), fromString("::").subtract(
+ Long.MAX_VALUE).subtract(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final long randomLong = random.nextLong();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomLong).subtract(randomLong));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Long.MAX_VALUE).subtract(Long.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Long.MIN_VALUE).subtract(Long.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(128));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3::"), fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(64));
+ assertEquals(fromString("2000::"), fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(15));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithPrefixLength(1));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithPrefixLength(128));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithPrefixLength(64));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithPrefixLength(1));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithPrefixLength(1));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+}
diff --git a/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..67d2675
--- /dev/null
+++ b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,25 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..e4bc68a
--- /dev/null
+++ b/tags/ipv6-0.1/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,42 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromFirstAndLastAddress()
+ {
+ assertEquals(new IPv6Network(fromString("::"), 126), new IPv6Network(fromString("::1"), fromString("::2")));
+ assertEquals(new IPv6Network(fromString("a:b::"), 44), new IPv6Network(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", new IPv6Network(fromString("::"), 126).toString());
+ assertEquals("a:b:c:d::/64", new IPv6Network(fromString("a:b:c:d::"), 64).toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = new IPv6Network(new IPv6Address(rg.nextLong(), rg.nextLong()), rg.nextInt(128) + 1);
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+}
diff --git a/tags/ipv6-0.2/pom.xml b/tags/ipv6-0.2/pom.xml
new file mode 100644
index 0000000..ffbcf0c
--- /dev/null
+++ b/tags/ipv6-0.2/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+ be.jvb.ipv6
+ ipv6
+ 0.2
+ jar
+ IPv6
+
+ https://java-ipv6.googlecode.com/svn/tags/ipv6-0.2
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/ipv6-0.2
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.7
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+
+
diff --git a/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6Address.java b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6Address.java
new file mode 100644
index 0000000..3655c0d
--- /dev/null
+++ b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6Address.java
@@ -0,0 +1,305 @@
+package be.jvb.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import static be.jvb.ipv6.IPv6AddressHelpers.expandShortNotation;
+import static be.jvb.ipv6.IPv6AddressHelpers.isLessThanUnsigned;
+import static be.jvb.ipv6.IPv6AddressHelpers.isZeroString;
+import static be.jvb.ipv6.IPv6AddressHelpers.mergeLongArrayIntoIPv6Address;
+import static be.jvb.ipv6.IPv6AddressHelpers.parseStringArrayIntoLongArray;
+import static java.lang.Long.numberOfTrailingZeros;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String longNotation = expandShortNotation(string);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(long value)
+ {
+ final long newLowBits = lowBits + value;
+ if ((lowBits < 0 && newLowBits >= 0) || (lowBits >= 0 && newLowBits < 0))
+ {
+ // sign changes indicate carry out -> carry out from low bits into high bits
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(long value)
+ {
+ final long newLowBits = lowBits - value;
+ if ((lowBits < 0 && newLowBits >= 0) || (lowBits >= 0 && newLowBits < 0))
+ {
+ // sign changes indicate carry out -> carry out from high bits into low bits
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+
+ /**
+ * Mask the address with the given prefix length.
+ *
+ * @param prefixLength prefix length
+ * @return an address of which the last 128 - prefixLength bits are zero
+ */
+ public IPv6Address maskWithPrefixLength(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ if (prefixLength == 128)
+ {
+ return this;
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (prefixLength > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - prefixLength)), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given prefix length.
+ *
+ * @param prefixLength prefix length
+ * @return an address of which the last 128 - prefixLength bits are one
+ */
+ public IPv6Address maximumAddressWithPrefixLength(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ if (prefixLength == 128)
+ {
+ return this;
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> prefixLength), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ final String[] strings = toStringArray();
+
+ final StringBuilder result = new StringBuilder();
+
+ boolean shortHandNotationUsed = false;
+ boolean shortHandNotationBusy = false;
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (!shortHandNotationUsed && i < N_SHORTS - 1 && isZeroString(strings[i]) && isZeroString(strings[i + 1]))
+ {
+ shortHandNotationUsed = true;
+ shortHandNotationBusy = true;
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(isZeroString(strings[i]) && shortHandNotationBusy))
+ {
+ shortHandNotationBusy = false;
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toStringArray()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%X", shorts[i]);
+ }
+ return strings;
+ }
+
+ public short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ? numberOfTrailingZeros(highBits) + 64 : numberOfTrailingZeros(lowBits);
+ }
+
+ int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+}
diff --git a/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..40e229f
--- /dev/null
+++ b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,110 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isZeroString(String string)
+ {
+ return "0".equals(string);
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressPool.java b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..36feb0e
--- /dev/null
+++ b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
@@ -0,0 +1,332 @@
+package be.jvb.ipv6;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final int prefixLength;
+
+ /**
+ * Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ * length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ * length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ * length).
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ */
+ public IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength)
+ {
+ // in the beginning, all is free
+ this(first, last, prefixLength, new TreeSet(Arrays.asList(new IPv6AddressRange(first, last))));
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges.
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength,
+ final SortedSet freeRanges)
+ {
+ this.underlyingRange = new IPv6AddressRange(first, last);
+
+ this.prefixLength = prefixLength;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+
+ validateFreeRanges(first, last, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(first, last, prefixLength);
+ }
+
+ private void validateFreeRanges(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(first, last, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be usefull. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(first) >= 0 && toValidate.last().getLast().compareTo(last) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(IPv6Address first, IPv6Address last, int prefixLength)
+ {
+ final int allocatableBits = 128 - prefixLength;
+
+ if (first.numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (last.numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = new IPv6Network(firstFreeRange.getFirst(), prefixLength);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (toAllocate.getPrefixLength() != this.prefixLength)
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /" + prefixLength);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges);
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (network.getPrefixLength() != prefixLength)
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getPrefixLength() + "] can not be free in a pool which uses prefix length [" +
+ prefixLength + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+}
diff --git a/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressRange.java b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..3ba031f
--- /dev/null
+++ b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
@@ -0,0 +1,194 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ public IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(new IPv6AddressRange(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(new IPv6AddressRange(first, last.subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, address.subtract(1)),
+ new IPv6AddressRange(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return new IPv6AddressRange(address, last);
+ else if (address.compareTo(last) > 0)
+ return new IPv6AddressRange(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(new IPv6AddressRange(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(new IPv6AddressRange(first, network.getLast().subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, network.getFirst().subtract(1)),
+ new IPv6AddressRange(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (this.first != that.first)
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ return current = current.add(1);
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6Network.java b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6Network.java
new file mode 100644
index 0000000..430f5cb
--- /dev/null
+++ b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6Network.java
@@ -0,0 +1,116 @@
+package be.jvb.ipv6;
+
+import static be.jvb.ipv6.IPv6NetworkHelpers.longestPrefixLength;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final int prefixLength;
+
+ /**
+ * Construct from address and prefix length.
+ *
+ * @param address address
+ * @param prefixLength prefix length, in range ]0, 128]
+ */
+ public IPv6Network(IPv6Address address, int prefixLength)
+ {
+ super(address.maskWithPrefixLength(prefixLength), address.maximumAddressWithPrefixLength(prefixLength));
+
+ this.address = address.maskWithPrefixLength(prefixLength);
+ this.prefixLength = prefixLength;
+ }
+
+ /**
+ * Construct from first and last address. This will construct the smallest possible network ("longest prefix length") which contains
+ * both addresses.
+ *
+ * @param first first address
+ * @param last last address
+ */
+ public IPv6Network(IPv6Address first, IPv6Address last)
+ {
+ super(first.maskWithPrefixLength(longestPrefixLength(first, last)),
+ first.maximumAddressWithPrefixLength(longestPrefixLength(first, last)));
+
+ this.prefixLength = longestPrefixLength(first, last);
+ this.address = this.getFirst();
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return new IPv6Network(networkAddress, prefixLength);
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + prefixLength;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (prefixLength != that.prefixLength) return false;
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = address != null ? address.hashCode() : 0;
+ result = 31 * result + prefixLength;
+ return result;
+ }
+
+ public int getPrefixLength()
+ {
+ return prefixLength;
+ }
+}
diff --git a/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..ca4259d
--- /dev/null
+++ b/tags/ipv6-0.2/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,30 @@
+package be.jvb.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = BitSet.valueOf(new long[]{first.getLowBits(), first.getHighBits()});
+ final BitSet lastBits = BitSet.valueOf(new long[]{last.getLowBits(), last.getHighBits()});
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet firstBits, BitSet lastBits)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (firstBits.get(i) == lastBits.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/Examples.java b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/Examples.java
new file mode 100644
index 0000000..686c2d4
--- /dev/null
+++ b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/Examples.java
@@ -0,0 +1,53 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+/**
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = new IPv6AddressRange(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6Network range = new IPv6Network(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"), 120);
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..41abad5
--- /dev/null
+++ b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,171 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ new IPv6AddressPool(fromString("2001::1"), fromString("2001::ffff:ffff"), 120);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:fffe"), 120);
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::ab00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::abcd:ef00"), fromString("2001::abcd:efff"), 120);
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(new IPv6Network(fromString("::1"), 128));
+
+ assertTrue(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("::1"), 128));
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate(new IPv6Network(fromString("::1"), 128)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(new IPv6Network(fromString("::99"), 128));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(new IPv6Network(fromString("::" + i), 128));
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::200"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+}
diff --git a/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..7c5d055
--- /dev/null
+++ b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,90 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ new IPv6AddressRange(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8")).size());
+ assertEquals(0, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4")).size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+}
diff --git a/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressTest.java b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..eb4594b
--- /dev/null
+++ b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
@@ -0,0 +1,221 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromInetAddress;
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Long.MAX_VALUE), fromString("::").add(Long.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Long.MAX_VALUE), fromString("::").subtract(
+ Long.MAX_VALUE).subtract(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final long randomLong = random.nextLong();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomLong).subtract(randomLong));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Long.MAX_VALUE).subtract(Long.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Long.MIN_VALUE).subtract(Long.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(128));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3::"), fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(64));
+ assertEquals(fromString("2000::"), fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(15));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithPrefixLength(1));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithPrefixLength(128));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithPrefixLength(64));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithPrefixLength(1));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithPrefixLength(1));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+}
diff --git a/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..67d2675
--- /dev/null
+++ b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,25 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..e4bc68a
--- /dev/null
+++ b/tags/ipv6-0.2/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,42 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromFirstAndLastAddress()
+ {
+ assertEquals(new IPv6Network(fromString("::"), 126), new IPv6Network(fromString("::1"), fromString("::2")));
+ assertEquals(new IPv6Network(fromString("a:b::"), 44), new IPv6Network(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", new IPv6Network(fromString("::"), 126).toString());
+ assertEquals("a:b:c:d::/64", new IPv6Network(fromString("a:b:c:d::"), 64).toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = new IPv6Network(new IPv6Address(rg.nextLong(), rg.nextLong()), rg.nextInt(128) + 1);
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+}
diff --git a/tags/ipv6-0.3/pom.xml b/tags/ipv6-0.3/pom.xml
new file mode 100644
index 0000000..3e6ec70
--- /dev/null
+++ b/tags/ipv6-0.3/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+ be.jvb.ipv6
+ ipv6
+ 0.3
+ jar
+ IPv6
+
+ https://java-ipv6.googlecode.com/svn/tags/ipv6-0.3
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/ipv6-0.3
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.7
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+
+
diff --git a/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6Address.java b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6Address.java
new file mode 100644
index 0000000..e173317
--- /dev/null
+++ b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6Address.java
@@ -0,0 +1,341 @@
+package be.jvb.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import static be.jvb.ipv6.IPv6AddressHelpers.expandShortNotation;
+import static be.jvb.ipv6.IPv6AddressHelpers.isLessThanUnsigned;
+import static be.jvb.ipv6.IPv6AddressHelpers.isZeroString;
+import static be.jvb.ipv6.IPv6AddressHelpers.mergeLongArrayIntoIPv6Address;
+import static be.jvb.ipv6.IPv6AddressHelpers.parseStringArrayIntoLongArray;
+import static java.lang.Long.numberOfTrailingZeros;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String longNotation = expandShortNotation(string);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something postive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given prefix length.
+ *
+ * @param prefixLength prefix length
+ * @return an address of which the last 128 - prefixLength bits are zero
+ */
+ public IPv6Address maskWithPrefixLength(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ if (prefixLength == 128)
+ {
+ return this;
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (prefixLength > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - prefixLength)), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given prefix length.
+ *
+ * @param prefixLength prefix length
+ * @return an address of which the last 128 - prefixLength bits are one
+ */
+ public IPv6Address maximumAddressWithPrefixLength(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ if (prefixLength == 128)
+ {
+ return this;
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> prefixLength), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ final String[] strings = toStringArray();
+
+ final StringBuilder result = new StringBuilder();
+
+ boolean shortHandNotationUsed = false;
+ boolean shortHandNotationBusy = false;
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (!shortHandNotationUsed && i < N_SHORTS - 1 && isZeroString(strings[i]) && isZeroString(strings[i + 1]))
+ {
+ shortHandNotationUsed = true;
+ shortHandNotationBusy = true;
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(isZeroString(strings[i]) && shortHandNotationBusy))
+ {
+ shortHandNotationBusy = false;
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toStringArray()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%X", shorts[i]);
+ }
+ return strings;
+ }
+
+ public short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ? numberOfTrailingZeros(highBits) + 64 : numberOfTrailingZeros(lowBits);
+ }
+
+ int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+}
diff --git a/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..40e229f
--- /dev/null
+++ b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,110 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isZeroString(String string)
+ {
+ return "0".equals(string);
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressPool.java b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..863f077
--- /dev/null
+++ b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
@@ -0,0 +1,459 @@
+package be.jvb.ipv6;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final int prefixLength;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ * length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ * length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ * length).
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ */
+ public IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength)
+ {
+ // in the beginning, all is free
+ this(first, last, prefixLength, new TreeSet(Arrays.asList(new IPv6AddressRange(first, last))), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = new IPv6AddressRange(first, last);
+
+ this.prefixLength = prefixLength;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(first, last, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(first, last, prefixLength);
+ }
+
+ private void validateFreeRanges(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(first, last, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be usefull. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(first) >= 0 && toValidate.last().getLast().compareTo(last) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(IPv6Address first, IPv6Address last, int prefixLength)
+ {
+ final int allocatableBits = 128 - prefixLength;
+
+ if (first.numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (last.numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = new IPv6Network(firstFreeRange.getFirst(), prefixLength);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (toAllocate.getPrefixLength() != this.prefixLength)
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /" + prefixLength);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (network.getPrefixLength() != prefixLength)
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getPrefixLength() + "] can not be free in a pool which uses prefix length [" +
+ prefixLength + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressPool)) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (prefixLength != that.prefixLength) return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + prefixLength;
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+}
diff --git a/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressRange.java b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..527eb98
--- /dev/null
+++ b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
@@ -0,0 +1,198 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ public IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(new IPv6AddressRange(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(new IPv6AddressRange(first, last.subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, address.subtract(1)),
+ new IPv6AddressRange(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return new IPv6AddressRange(address, last);
+ else if (address.compareTo(last) > 0)
+ return new IPv6AddressRange(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(new IPv6AddressRange(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(new IPv6AddressRange(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, network.getFirst().subtract(1)),
+ new IPv6AddressRange(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (this.first != that.first)
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ return current = current.add(1);
+ else
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6Network.java b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6Network.java
new file mode 100644
index 0000000..430f5cb
--- /dev/null
+++ b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6Network.java
@@ -0,0 +1,116 @@
+package be.jvb.ipv6;
+
+import static be.jvb.ipv6.IPv6NetworkHelpers.longestPrefixLength;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final int prefixLength;
+
+ /**
+ * Construct from address and prefix length.
+ *
+ * @param address address
+ * @param prefixLength prefix length, in range ]0, 128]
+ */
+ public IPv6Network(IPv6Address address, int prefixLength)
+ {
+ super(address.maskWithPrefixLength(prefixLength), address.maximumAddressWithPrefixLength(prefixLength));
+
+ this.address = address.maskWithPrefixLength(prefixLength);
+ this.prefixLength = prefixLength;
+ }
+
+ /**
+ * Construct from first and last address. This will construct the smallest possible network ("longest prefix length") which contains
+ * both addresses.
+ *
+ * @param first first address
+ * @param last last address
+ */
+ public IPv6Network(IPv6Address first, IPv6Address last)
+ {
+ super(first.maskWithPrefixLength(longestPrefixLength(first, last)),
+ first.maximumAddressWithPrefixLength(longestPrefixLength(first, last)));
+
+ this.prefixLength = longestPrefixLength(first, last);
+ this.address = this.getFirst();
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return new IPv6Network(networkAddress, prefixLength);
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + prefixLength;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (prefixLength != that.prefixLength) return false;
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = address != null ? address.hashCode() : 0;
+ result = 31 * result + prefixLength;
+ return result;
+ }
+
+ public int getPrefixLength()
+ {
+ return prefixLength;
+ }
+}
diff --git a/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..ca4259d
--- /dev/null
+++ b/tags/ipv6-0.3/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,30 @@
+package be.jvb.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = BitSet.valueOf(new long[]{first.getLowBits(), first.getHighBits()});
+ final BitSet lastBits = BitSet.valueOf(new long[]{last.getLowBits(), last.getHighBits()});
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet firstBits, BitSet lastBits)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (firstBits.get(i) == lastBits.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/Examples.java b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/Examples.java
new file mode 100644
index 0000000..686c2d4
--- /dev/null
+++ b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/Examples.java
@@ -0,0 +1,53 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+/**
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = new IPv6AddressRange(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6Network range = new IPv6Network(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"), 120);
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..8aed941
--- /dev/null
+++ b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,224 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ new IPv6AddressPool(fromString("2001::1"), fromString("2001::ffff:ffff"), 120);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:fffe"), 120);
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::ab00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::abcd:ef00"), fromString("2001::abcd:efff"), 120);
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(new IPv6Network(fromString("::1"), 128));
+
+ assertTrue(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("2001::"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("2001::100"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("2001::200"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("::1"), 128));
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate(new IPv6Network(fromString("::1"), 128)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(new IPv6Network(fromString("::99"), 128));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(new IPv6Network(fromString("::" + i), 128));
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::"), 120));
+ assertEquals(new IPv6Network(fromString("2001::"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::200"), 120));
+ assertEquals(new IPv6Network(fromString("2001::200"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::100"), 120));
+ assertEquals(new IPv6Network(fromString("2001::100"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), i);
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::"), i), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::").maximumAddressWithPrefixLength(i).add(1), i), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::"), fromString("::ffff:ffff:ffff:ffff"), i);
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::"), i), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::").maximumAddressWithPrefixLength(i).add(1), i), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(fromString("::"), fromString("::ffff:ffff:ffff:ffff"), 66);
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..7c5d055
--- /dev/null
+++ b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,90 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ new IPv6AddressRange(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8")).size());
+ assertEquals(0, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4")).size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+}
diff --git a/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressTest.java b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..5b4d9dd
--- /dev/null
+++ b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
@@ -0,0 +1,220 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromInetAddress;
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(128));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3::"), fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(64));
+ assertEquals(fromString("2000::"), fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithPrefixLength(15));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithPrefixLength(1));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithPrefixLength(128));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithPrefixLength(120));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithPrefixLength(64));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithPrefixLength(1));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithPrefixLength(1));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+}
diff --git a/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..67d2675
--- /dev/null
+++ b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,25 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..e4bc68a
--- /dev/null
+++ b/tags/ipv6-0.3/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,42 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromFirstAndLastAddress()
+ {
+ assertEquals(new IPv6Network(fromString("::"), 126), new IPv6Network(fromString("::1"), fromString("::2")));
+ assertEquals(new IPv6Network(fromString("a:b::"), 44), new IPv6Network(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", new IPv6Network(fromString("::"), 126).toString());
+ assertEquals("a:b:c:d::/64", new IPv6Network(fromString("a:b:c:d::"), 64).toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = new IPv6Network(new IPv6Address(rg.nextLong(), rg.nextLong()), rg.nextInt(128) + 1);
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+}
diff --git a/tags/ipv6-0.4/pom.xml b/tags/ipv6-0.4/pom.xml
new file mode 100644
index 0000000..15ee01f
--- /dev/null
+++ b/tags/ipv6-0.4/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+ be.jvb.ipv6
+ ipv6
+ 0.4
+ jar
+ IPv6
+
+ https://java-ipv6.googlecode.com/svn/tags/ipv6-0.4
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/ipv6-0.4
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.7
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+
+
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6Address.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6Address.java
new file mode 100644
index 0000000..c837cac
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6Address.java
@@ -0,0 +1,349 @@
+package be.jvb.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import static be.jvb.ipv6.IPv6AddressHelpers.*;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ public IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String longNotation = expandShortNotation(string);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something postive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are zero
+ */
+ public IPv6Address maskWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - networkMask.asPrefixLength())), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are one
+ */
+ public IPv6Address maximumAddressWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> networkMask.asPrefixLength()), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ /**
+ * @return String representation of the IPv6 Address, using shorthand notation whenever possible.
+ */
+ @Override
+ public String toString()
+ {
+ final String[] strings = toStringArray();
+
+ final StringBuilder result = new StringBuilder();
+
+ boolean shortHandNotationUsed = false;
+ boolean shortHandNotationBusy = false;
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (!shortHandNotationUsed && i < N_SHORTS - 1 && isZeroString(strings[i]) && isZeroString(strings[i + 1]))
+ {
+ shortHandNotationUsed = true;
+ shortHandNotationBusy = true;
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(isZeroString(strings[i]) && shortHandNotationBusy))
+ {
+ shortHandNotationBusy = false;
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toStringArray()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%X", shorts[i]);
+ }
+ return strings;
+ }
+
+ public short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ public int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ?
+ Long.numberOfTrailingZeros(highBits) + 64 :
+ Long.numberOfTrailingZeros(lowBits);
+ }
+
+ public int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+ public int numberOfLeadingZeroes()
+ {
+ return highBits == 0 ?
+ Long.numberOfLeadingZeros(lowBits) + 64 :
+ Long.numberOfLeadingZeros(highBits);
+ }
+
+ public int numberOfLeadingOnes()
+ {
+ // count leading ones in "value" by counting leading zeroes in "~ value"
+ final IPv6Address flipped = new IPv6Address(~this.highBits, ~this.lowBits);
+ return flipped.numberOfLeadingZeroes();
+ }
+
+}
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..40e229f
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,110 @@
+package be.jvb.ipv6;
+
+import java.util.Arrays;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isZeroString(String string)
+ {
+ return "0".equals(string);
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressPool.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..162b9e1
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressPool.java
@@ -0,0 +1,453 @@
+package be.jvb.ipv6;
+
+
+import java.util.*;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final int prefixLength;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool in between the given first and last address (inclusive) which is completely free. The given prefix length is the prefix
+ * length used for allocating subnets from this range. The whole range should be "aligned" on a multiple of subnets of this prefix
+ * length (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given prefix
+ * length).
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ */
+ public IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength)
+ {
+ // in the beginning, all is free
+ this(first, last, prefixLength, new TreeSet(Arrays.asList(new IPv6AddressRange(first, last))), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param first first ip address of the range
+ * @param last last ip address of the range
+ * @param prefixLength prefix length with which to allocate subnets from this range
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6Address first, final IPv6Address last, final int prefixLength,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = new IPv6AddressRange(first, last);
+
+ this.prefixLength = prefixLength;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(first, last, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(first, last, prefixLength);
+ }
+
+ private void validateFreeRanges(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(first, last, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be usefull. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6Address first, IPv6Address last, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(first) >= 0 && toValidate.last().getLast().compareTo(last) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenPrefixLength(IPv6Address first, IPv6Address last, int prefixLength)
+ {
+ final int allocatableBits = 128 - prefixLength;
+
+ if (first.numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (last.numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + prefixLength + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = new IPv6Network(firstFreeRange.getFirst(), prefixLength);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (toAllocate.getPrefixLength() != this.prefixLength)
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /" + prefixLength);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(new IPv6AddressRange(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(new IPv6AddressRange(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(getFirst(), getLast(), prefixLength, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (network.getPrefixLength() != prefixLength)
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getPrefixLength() + "] can not be free in a pool which uses prefix length [" +
+ prefixLength + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressPool)) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (prefixLength != that.prefixLength) return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + prefixLength;
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+}
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressRange.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..59331f2
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6AddressRange.java
@@ -0,0 +1,194 @@
+package be.jvb.ipv6;
+
+import java.util.*;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ public IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(new IPv6AddressRange(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(new IPv6AddressRange(first, last.subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, address.subtract(1)),
+ new IPv6AddressRange(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return new IPv6AddressRange(address, last);
+ else if (address.compareTo(last) > 0)
+ return new IPv6AddressRange(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(new IPv6AddressRange(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(new IPv6AddressRange(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(new IPv6AddressRange(first, network.getFirst().subtract(1)),
+ new IPv6AddressRange(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (this.first != that.first)
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ return current = current.add(1);
+ else
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6Network.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6Network.java
new file mode 100644
index 0000000..04315a7
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6Network.java
@@ -0,0 +1,140 @@
+package be.jvb.ipv6;
+
+import static be.jvb.ipv6.IPv6NetworkHelpers.longestPrefixLength;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final IPv6NetworkMask networkMask;
+
+ /**
+ * Construct from address and network mask.
+ *
+ * @param address address
+ * @param networkMask network mask
+ */
+ public IPv6Network(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ super(address.maskWithNetworkMask(networkMask), address.maximumAddressWithNetworkMask(networkMask));
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Construct from address and prefix length.
+ *
+ * @param address address
+ * @param prefixLength prefix length
+ */
+ public IPv6Network(IPv6Address address, int prefixLength)
+ {
+ super(address.maskWithNetworkMask(new IPv6NetworkMask(prefixLength)),
+ address.maximumAddressWithNetworkMask(new IPv6NetworkMask(prefixLength)));
+
+ final IPv6NetworkMask networkMask = new IPv6NetworkMask(prefixLength);
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Construct from first and last address. This will construct the smallest possible network ("longest prefix length") which contains
+ * both addresses.
+ *
+ * @param first first address
+ * @param last last address
+ */
+ public IPv6Network(IPv6Address first, IPv6Address last)
+ {
+ super(first.maskWithNetworkMask(new IPv6NetworkMask(longestPrefixLength(first, last))),
+ first.maximumAddressWithNetworkMask(new IPv6NetworkMask(longestPrefixLength(first, last))));
+
+ this.networkMask = new IPv6NetworkMask(longestPrefixLength(first, last));
+ this.address = this.getFirst();
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return new IPv6Network(networkAddress, new IPv6NetworkMask(prefixLength));
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + networkMask.asPrefixLength();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+ if (networkMask != null ? !networkMask.equals(that.networkMask) : that.networkMask != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = super.hashCode();
+ result = 31 * result + (address != null ? address.hashCode() : 0);
+ result = 31 * result + (networkMask != null ? networkMask.hashCode() : 0);
+ return result;
+ }
+
+ public int getPrefixLength()
+ {
+ return networkMask.asPrefixLength();
+ }
+
+ public IPv6NetworkMask getNetmask()
+ {
+ return networkMask;
+ }
+}
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..ad11633
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,30 @@
+package be.jvb.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = BitSet.valueOf(new long[]{first.getLowBits(), first.getHighBits()});
+ final BitSet lastBits = BitSet.valueOf(new long[]{last.getLowBits(), last.getHighBits()});
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet a, BitSet b)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (a.get(i) == b.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6NetworkMask.java b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6NetworkMask.java
new file mode 100644
index 0000000..8b0d469
--- /dev/null
+++ b/tags/ipv6-0.4/src/main/java/be/jvb/ipv6/IPv6NetworkMask.java
@@ -0,0 +1,120 @@
+package be.jvb.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ * starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6NetworkMask
+{
+ private final int prefixLength;
+
+ /**
+ * Construct an IPv6 network mask from an IPv6 address. The address should be a valid network mask.
+ *
+ * @param iPv6Address address to use as network mask
+ * @throws IllegalArgumentException if the address is not a valid network mask
+ */
+ public IPv6NetworkMask(final IPv6Address iPv6Address)
+ {
+ validateNetworkMask(iPv6Address);
+ this.prefixLength = iPv6Address.numberOfLeadingOnes();
+ }
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ public IPv6NetworkMask(int prefixLength)
+ {
+ if (prefixLength <= 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval ]0, 128]");
+
+ this.prefixLength = prefixLength;
+ }
+
+ private static void validateNetworkMask(IPv6Address addressToValidate)
+ {
+ final BitSet addressAsBitSet = BitSet.valueOf(new long[]{addressToValidate.getLowBits(), addressToValidate.getHighBits()});
+ if (!addressAsBitSet.get(127))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ else
+ {
+ boolean firstZeroFound = false;
+ for (int i = 127; i >= 0 && !firstZeroFound; i--)
+ {
+ if (!addressAsBitSet.get(i))
+ {
+ firstZeroFound = true;
+
+ // a zero -> all the others should also be zero
+ for (int j = i - 1; j >= 0; j--)
+ {
+ if (addressAsBitSet.get(j))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int asPrefixLength()
+ {
+ return prefixLength;
+ }
+
+ public IPv6Address asAddress()
+ {
+ if (prefixLength == 128)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0L);
+ }
+ else if (prefixLength > 64)
+ {
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL << (64 - prefixLength), 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6NetworkMask that = (IPv6NetworkMask) o;
+
+ if (prefixLength != that.prefixLength) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefixLength;
+ }
+
+ @Override
+ public String toString()
+ {
+ return asAddress().toString();
+ }
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..5173bb3
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,223 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ new IPv6AddressPool(fromString("2001::1"), fromString("2001::ffff:ffff"), 120);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:fffe"), 120);
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::ab00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::0"), fromString("2001::ffff:ffff"), 120);
+ new IPv6AddressPool(fromString("2001::abcd:ef00"), fromString("2001::abcd:efff"), 120);
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(new IPv6Network(fromString("::1"), 128));
+
+ assertTrue(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("2001::"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("2001::100"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("2001::200"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("::1"), 128));
+
+ assertFalse(pool.isFree(new IPv6Network(fromString("::1"), 128)));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate(new IPv6Network(fromString("::1"), 128)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::1"), 128);
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(new IPv6Network(fromString("::99"), 128));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::1"), fromString("::5"), 128);
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(new IPv6Network(fromString("::" + i), 128));
+ assertFalse(pool.isFree(new IPv6Network(fromString("::" + i), 128)));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("2001::"), fromString("2001::ffff:ffff"), 120);
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::"), 120));
+ assertEquals(new IPv6Network(fromString("2001::"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::200"), 120));
+ assertEquals(new IPv6Network(fromString("2001::200"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ pool = pool.allocate(new IPv6Network(fromString("2001::100"), 120));
+ assertEquals(new IPv6Network(fromString("2001::100"), 120), pool.getLastAllocated());
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::ffff:0"), 120)));
+
+ pool = pool.deAllocate(new IPv6Network(fromString("2001::100"), 120));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::"), 120)));
+ assertTrue(pool.isFree(new IPv6Network(fromString("2001::100"), 120)));
+ assertFalse(pool.isFree(new IPv6Network(fromString("2001::200"), 120)));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), i);
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::"), i), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1), i),
+ pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = new IPv6AddressPool(fromString("::"), fromString("::ffff:ffff:ffff:ffff"), i);
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::"), i), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(new IPv6Network(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1), i),
+ pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(fromString("::"), fromString("::ffff:ffff:ffff:ffff"), 66);
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..7c5d055
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,90 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ new IPv6AddressRange(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(new IPv6AddressRange(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(new IPv6AddressRange(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4")).size());
+ assertEquals(1, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8")).size());
+ assertEquals(0, new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4")).size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : new IPv6AddressRange(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressTest.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..03ec8cc
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6AddressTest.java
@@ -0,0 +1,251 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromInetAddress;
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("2000::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(15)));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+ @Test
+ public void numberOfLeadingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfLeadingOnes());
+ assertEquals(1, fromString("8000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::f").numberOfLeadingOnes());
+ assertEquals(65, fromString("ffff:ffff:ffff:ffff:8000::f").numberOfLeadingOnes());
+ }
+
+ @Test
+ public void numberOfTrailingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfTrailingZeroes());
+ assertEquals(127, fromString("8000::").numberOfTrailingZeroes());
+ assertEquals(124, fromString("f000::").numberOfTrailingZeroes());
+ assertEquals(0, fromString("f000::f").numberOfTrailingZeroes());
+ assertEquals(63, fromString("ffff:ffff:ffff:ffff:8000::").numberOfTrailingZeroes());
+ }
+
+ @Test
+ public void numberOfLeadingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfLeadingZeroes());
+ assertEquals(0, fromString("8000::").numberOfLeadingZeroes());
+ assertEquals(124, fromString("::f").numberOfLeadingZeroes());
+ assertEquals(63, fromString("::1:ffff:ffff:ffff:ffff").numberOfLeadingZeroes());
+ }
+
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..67d2675
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,25 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkMaskTest.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkMaskTest.java
new file mode 100644
index 0000000..cdc5794
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkMaskTest.java
@@ -0,0 +1,42 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkMaskTest
+{
+ @Test
+ public void constructValidNetworkMasks()
+ {
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)), new IPv6NetworkMask(128));
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)), new IPv6NetworkMask(127));
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffcL)), new IPv6NetworkMask(126));
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)), new IPv6NetworkMask(65));
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0x0L)), new IPv6NetworkMask(64));
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xc000000000000000L, 0x0L)), new IPv6NetworkMask(2));
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0x8000000000000000L, 0x0L)), new IPv6NetworkMask(1));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_Zero()
+ {
+ new IPv6NetworkMask(0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_TooBig()
+ {
+ new IPv6NetworkMask(129);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromAddress()
+ {
+ new IPv6NetworkMask(new IPv6Address(123L, 456L));
+ }
+
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..1745168
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,68 @@
+package be.jvb.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static be.jvb.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromFirstAndLastAddress()
+ {
+ assertEquals(new IPv6Network(fromString("::"), 126), new IPv6Network(fromString("::1"), fromString("::2")));
+ assertEquals(new IPv6Network(fromString("a:b::"), 44), new IPv6Network(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", new IPv6Network(fromString("::"), 126).toString());
+ assertEquals("a:b:c:d::/64", new IPv6Network(fromString("a:b:c:d::"), 64).toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = new IPv6Network(new IPv6Address(rg.nextLong(), rg.nextLong()), rg.nextInt(128) + 1);
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+ @Test
+ public void constructAndVerifyPrefixLength()
+ {
+ assertEquals(1, IPv6Network.fromString("a:b:c::/1").getPrefixLength());
+ assertEquals(63, IPv6Network.fromString("a:b:c::/63").getPrefixLength());
+ assertEquals(64, IPv6Network.fromString("a:b:c::/64").getPrefixLength());
+ assertEquals(65, IPv6Network.fromString("a:b:c::/65").getPrefixLength());
+ assertEquals(127, IPv6Network.fromString("a:b:c::/127").getPrefixLength());
+ assertEquals(128, IPv6Network.fromString("a:b:c::/128").getPrefixLength());
+ }
+
+ @Test
+ public void constructAndVerifyNetmask()
+ {
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0x8000000000000000L, 0x0L)), IPv6Network.fromString("a:b:c::/1").getNetmask());
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xfffffffffffffffeL, 0x0L)), IPv6Network.fromString("a:b:c::/63").getNetmask());
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0x0L)), IPv6Network.fromString("a:b:c::/64").getNetmask());
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)),
+ IPv6Network.fromString("a:b:c::/65").getNetmask());
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)),
+ IPv6Network.fromString("a:b:c::/127").getNetmask());
+ assertEquals(new IPv6NetworkMask(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)),
+ IPv6Network.fromString("a:b:c::/128").getNetmask());
+ }
+
+
+}
diff --git a/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/examples/Examples.java b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/examples/Examples.java
new file mode 100644
index 0000000..6e67bfd
--- /dev/null
+++ b/tags/ipv6-0.4/src/test/java/be/jvb/ipv6/examples/Examples.java
@@ -0,0 +1,93 @@
+package be.jvb.ipv6.examples;
+
+import be.jvb.ipv6.*;
+import org.junit.Test;
+
+/**
+ * Some examples also featured in the online documentation. This class is in a separate package on purpose, such that we make sure only
+ * to call methods of the public API.
+ *
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = new IPv6AddressRange(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6Network range = new IPv6Network(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void ipNetworkCalculation()
+ {
+ final IPv6Network strangeNetwork = IPv6Network.fromString("fe80::226:2dff:fefa:cd1f/43");
+
+ System.out.println(strangeNetwork.getPrefixLength()); // prints 43
+ System.out.println(strangeNetwork.getFirst()); // prints fe80::
+ System.out.println(strangeNetwork.getLast()); // prints fe80:0:1f:ffff:ffff:ffff:ffff:ffff
+ System.out.println(strangeNetwork.getNetmask()); // prints ffff:ffff:ffe0::
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void ipNetworkMaskConstruction()
+ {
+ final IPv6NetworkMask slash40Network = new IPv6NetworkMask(40);
+ System.out.println(slash40Network.asAddress()); // prints ffff:ffff:ff00::
+ System.out.println(slash40Network.asPrefixLength()); // prints 40
+
+ final IPv6NetworkMask slash40NetworkConstructedFromAddressNotation = new IPv6NetworkMask(IPv6Address.fromString("ffff:ffff:ff00::"));
+ System.out.println(slash40Network.equals(slash40NetworkConstructedFromAddressNotation)); // prints true
+
+ final IPv6NetworkMask invalidNetworkMask = new IPv6NetworkMask(IPv6Address.fromString("0fff::")); // fails
+ }
+
+ @Test
+ public void ipAddressNetworkMasking()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+
+ final IPv6Address masked = iPv6Address.maskWithNetworkMask(new IPv6NetworkMask(40));
+ System.out.println(masked.toString()); // prints fe80::
+
+ final IPv6Address maximum = iPv6Address.maximumAddressWithNetworkMask(new IPv6NetworkMask(40));
+ System.out.println(maximum.toString()); // prints fe80:0:ff:ffff:ffff:ffff:ffff:ffff
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = new IPv6AddressPool(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"), 120);
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/LICENSE b/tags/java-ipv6-0.10/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tags/java-ipv6-0.10/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/java-ipv6-0.10/NOTICE b/tags/java-ipv6-0.10/NOTICE
new file mode 100644
index 0000000..a4605c4
--- /dev/null
+++ b/tags/java-ipv6-0.10/NOTICE
@@ -0,0 +1,2 @@
+ Java IPv6
+ Copyright 2013 Jan Van Besien
\ No newline at end of file
diff --git a/tags/java-ipv6-0.10/pom.xml b/tags/java-ipv6-0.10/pom.xml
new file mode 100644
index 0000000..c550679
--- /dev/null
+++ b/tags/java-ipv6-0.10/pom.xml
@@ -0,0 +1,143 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ 0.10
+ jar
+ Java IPv6 Library
+ http://code.google.com/p/java-ipv6
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.10
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.10
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.10
+
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+
+
+ ${basedir}
+ false
+
+ LICENSE
+ NOTICE
+
+
+
+
+
+
+
+ sonatype-oss-release
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 1.0-beta-4
+
+
+ validate
+
+ create
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/BitSetHelpers.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
new file mode 100644
index 0000000..698c51e
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * This class contains some helpers for working with BitSets. These are generally not necessary in JDK7, since the BitSet.valueOf(long[])
+ * method. However, for java-6 compatibility, we go this way.
+ *
+ * @author Jan Van Besien
+ */
+class BitSetHelpers
+{
+ static BitSet bitSetOf(long lowerBits, long upperBits)
+ {
+ final BitSet bitSet = new BitSet();
+ convert(lowerBits, 0, bitSet);
+ convert(upperBits, Long.SIZE, bitSet);
+ return bitSet;
+ }
+
+ static void convert(long value, int bitSetOffset, BitSet bits)
+ {
+ int index = 0;
+ while (value != 0L)
+ {
+ if (value % 2L != 0)
+ {
+ bits.set(bitSetOffset + index);
+ }
+ ++index;
+ value = value >>> 1;
+ }
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6Address.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6Address.java
new file mode 100644
index 0000000..5776bfe
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6Address.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ /**
+ * Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits.
+ *
+ * @param highBits highest order bits
+ * @param lowBits lowest order bits
+ */
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String longNotation = IPv6AddressHelpers.expandShortNotation(string);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return IPv6AddressHelpers.mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return IPv6AddressHelpers.parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something postive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are zero
+ */
+ public IPv6Address maskWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (networkMask.asPrefixLength() == 0)
+ {
+ return new IPv6Address(0, 0);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - networkMask.asPrefixLength())), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are one
+ */
+ public IPv6Address maximumAddressWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> networkMask.asPrefixLength()), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ /**
+ * @return String representation of the IPv6 address, using shorthand notation whenever possible.
+ */
+ @Override
+ public String toString()
+ {
+ final String[] strings = toArrayOfShortStrings();
+
+ final StringBuilder result = new StringBuilder();
+
+ boolean shortHandNotationUsed = false;
+ boolean shortHandNotationBusy = false;
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (!shortHandNotationUsed && i < N_SHORTS - 1 && IPv6AddressHelpers.isZeroString(strings[i]) && IPv6AddressHelpers
+ .isZeroString(strings[i + 1]))
+ {
+ shortHandNotationUsed = true;
+ shortHandNotationBusy = true;
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(IPv6AddressHelpers.isZeroString(strings[i]) && shortHandNotationBusy))
+ {
+ shortHandNotationBusy = false;
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toArrayOfShortStrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%x", shorts[i]);
+ }
+ return strings;
+ }
+
+ /**
+ * @return String representation of the IPv6 address, never using shorthand notation.
+ */
+ public String toLongString()
+ {
+ final String[] strings = toArrayOfZeroPaddedstrings();
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < strings.length - 1; i++)
+ {
+ result.append(strings[i]).append(":");
+ }
+
+ result.append(strings[strings.length - 1]);
+
+ return result.toString();
+ }
+
+ private String[] toArrayOfZeroPaddedstrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%04x", shorts[i]);
+ }
+ return strings;
+ }
+
+ public short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ public int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ?
+ Long.numberOfTrailingZeros(highBits) + 64 :
+ Long.numberOfTrailingZeros(lowBits);
+ }
+
+ public int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+ public int numberOfLeadingZeroes()
+ {
+ return highBits == 0 ?
+ Long.numberOfLeadingZeros(lowBits) + 64 :
+ Long.numberOfLeadingZeros(highBits);
+ }
+
+ public int numberOfLeadingOnes()
+ {
+ // count leading ones in "value" by counting leading zeroes in "~ value"
+ final IPv6Address flipped = new IPv6Address(~this.highBits, ~this.lowBits);
+ return flipped.numberOfLeadingZeroes();
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..edf129a
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.Arrays;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isZeroString(String string)
+ {
+ return "0".equals(string);
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..77b9a0f
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+
+import java.util.*;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final IPv6NetworkMask allocationSubnetSize;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ * size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ * (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ */
+ public static IPv6AddressPool fromRangeAndSubnet(final IPv6AddressRange range,
+ final IPv6NetworkMask allocationSubnetSize)
+ {
+ // in the beginning, all is free
+ return new IPv6AddressPool(range, allocationSubnetSize, new TreeSet(Arrays.asList(range)), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6AddressRange range, final IPv6NetworkMask allocationSubnetSize,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = range;
+
+ this.allocationSubnetSize = allocationSubnetSize;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(underlyingRange, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenSize(underlyingRange, allocationSubnetSize);
+ }
+
+ private void validateFreeRanges(IPv6AddressRange range, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(range, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be useful. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6AddressRange range, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(range.getFirst()) >= 0
+ && toValidate.last().getLast().compareTo(range.getLast()) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenSize(IPv6AddressRange range, IPv6NetworkMask allocationSubnetSize)
+ {
+ final int allocatableBits = 128 - allocationSubnetSize.asPrefixLength();
+
+ if (range.getFirst().numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength() + "], "
+ + "first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (range.getLast().numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength()
+ + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = IPv6Network.fromAddressAndMask(firstFreeRange.getFirst(), allocationSubnetSize);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (!this.allocationSubnetSize.equals(toAllocate.getNetmask()))
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getNetmask().asPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /"
+ + allocationSubnetSize);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (!this.allocationSubnetSize.equals(network.getNetmask()))
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getNetmask().asPrefixLength()
+ + "] can not be free in a pool which uses prefix length [" +
+ allocationSubnetSize + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (allocationSubnetSize != null ? !allocationSubnetSize.equals(that.allocationSubnetSize) : that.allocationSubnetSize != null)
+ return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + (allocationSubnetSize != null ? allocationSubnetSize.hashCode() : 0);
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return underlyingRange.toLongString();
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..309335e
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.*;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public static IPv6AddressRange fromFirstAndLast(IPv6Address first, IPv6Address last)
+ {
+ return new IPv6AddressRange(first, last);
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(fromFirstAndLast(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(fromFirstAndLast(first, last.subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, address.subtract(1)),
+ fromFirstAndLast(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return fromFirstAndLast(address, last);
+ else if (address.compareTo(last) > 0)
+ return fromFirstAndLast(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(fromFirstAndLast(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(fromFirstAndLast(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, network.getFirst().subtract(1)),
+ fromFirstAndLast(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return first.toLongString() + " - " + last.toLongString();
+ }
+
+ /**
+ * The natural order of {@link com.googlecode.ipv6.IPv6AddressRange}s orders them on increasing first addresses, and on increasing last
+ * address if the first address would be equal.
+ *
+ * Note that the natural order does thus not compare sizes of ranges.
+ *
+ * @param that range to compare with
+ * @return negative, zero or positive depending on whether this is smaller, equal or greater than that
+ */
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (!this.first.equals(that.first))
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ return current = current.add(1);
+ else
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6Network.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6Network.java
new file mode 100644
index 0000000..60aa407
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6Network.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final IPv6NetworkMask networkMask;
+
+ /**
+ * Construct from address and network mask.
+ *
+ * @param address address
+ * @param networkMask network mask
+ */
+ private IPv6Network(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ super(address.maskWithNetworkMask(networkMask), address.maximumAddressWithNetworkMask(networkMask));
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Create an IPv6 network from an IPv6Address and an IPv6NetworkMask
+ *
+ * @param address IPv6 address (the network address or any other address within the network)
+ * @param networkMask IPv6 network mask
+ * @return IPv6 network
+ */
+ public static IPv6Network fromAddressAndMask(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ return new IPv6Network(address, networkMask);
+ }
+
+ /**
+ * Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ * length") which contains both addresses.
+ *
+ * @param one address one
+ * @param two address two, should be bigger than address one
+ */
+ public static IPv6Network fromTwoAddresses(IPv6Address one, IPv6Address two)
+ {
+ final IPv6NetworkMask longestPrefixLength = IPv6NetworkMask.fromPrefixLength(IPv6NetworkHelpers.longestPrefixLength(one, two));
+ return new IPv6Network(one.maskWithNetworkMask(longestPrefixLength), longestPrefixLength);
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 network
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return fromAddressAndMask(networkAddress, new IPv6NetworkMask(prefixLength));
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + networkMask.asPrefixLength();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return address.toLongString() + "/" + networkMask.asPrefixLength();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+ if (networkMask != null ? !networkMask.equals(that.networkMask) : that.networkMask != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = super.hashCode();
+ result = 31 * result + (address != null ? address.hashCode() : 0);
+ result = 31 * result + (networkMask != null ? networkMask.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6NetworkMask getNetmask()
+ {
+ return networkMask;
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..66268ed
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = bitSetOf(first.getLowBits(), first.getHighBits());
+ final BitSet lastBits = bitSetOf(last.getLowBits(), last.getHighBits());
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet a, BitSet b)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (a.get(i) == b.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
new file mode 100644
index 0000000..ee4394b
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ * starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6NetworkMask
+{
+ private final int prefixLength;
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ IPv6NetworkMask(int prefixLength)
+ {
+ if (prefixLength < 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval [0, 128]");
+
+ this.prefixLength = prefixLength;
+ }
+
+
+ /**
+ * Construct an IPv6 network mask from an IPv6 address. The address should be a valid network mask.
+ *
+ * @param iPv6Address address to use as network mask
+ * @throws IllegalArgumentException if the address is not a valid network mask
+ */
+ public static IPv6NetworkMask fromAddress(final IPv6Address iPv6Address)
+ {
+ validateNetworkMask(iPv6Address);
+ return new IPv6NetworkMask(iPv6Address.numberOfLeadingOnes());
+ }
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ public static IPv6NetworkMask fromPrefixLength(int prefixLength)
+ {
+ return new IPv6NetworkMask(prefixLength);
+ }
+
+ private static void validateNetworkMask(IPv6Address addressToValidate)
+ {
+ final BitSet addressAsBitSet = bitSetOf(addressToValidate.getLowBits(), addressToValidate.getHighBits());
+ if (!addressAsBitSet.get(127))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ else
+ {
+ boolean firstZeroFound = false;
+ for (int i = 127; i >= 0 && !firstZeroFound; i--)
+ {
+ if (!addressAsBitSet.get(i))
+ {
+ firstZeroFound = true;
+
+ // a zero -> all the others should also be zero
+ for (int j = i - 1; j >= 0; j--)
+ {
+ if (addressAsBitSet.get(j))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int asPrefixLength()
+ {
+ return prefixLength;
+ }
+
+ public IPv6Address asAddress()
+ {
+ if (prefixLength == 128)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0L);
+ }
+ else if (prefixLength > 64)
+ {
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL << (64 - prefixLength), 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6NetworkMask that = (IPv6NetworkMask) o;
+
+ if (prefixLength != that.prefixLength) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefixLength;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "" + prefixLength;
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..834d561
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::1"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:fffe")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::ab00"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("2001::ffff:ffff")), new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::abcd:ef00"),
+ fromString("2001::abcd:efff")), new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null",
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::99"), IPv6NetworkMask.fromPrefixLength(128)));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(66));
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..e699741
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ IPv6AddressRange.fromFirstAndLast(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8"))
+ .size());
+ assertEquals(0, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4"))
+ .size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+ @Test
+ public void compareTo()
+ {
+ final IPv6AddressRange a =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange b =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange c =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange d =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+
+ Assert.assertTrue(a.compareTo(b) > 0);
+ Assert.assertTrue(a.compareTo(c) < 0);
+ Assert.assertTrue(a.compareTo(d) < 0);
+ Assert.assertTrue(b.compareTo(c) < 0);
+ Assert.assertTrue(b.compareTo(d) < 0);
+ Assert.assertTrue(c.compareTo(d) > 0);
+
+ Assert.assertTrue(a.compareTo(a) == 0);
+ Assert.assertTrue(b.compareTo(b) == 0);
+ Assert.assertTrue(c.compareTo(c) == 0);
+ Assert.assertTrue(d.compareTo(d) == 0);
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..f13e41a
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromInetAddress;
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test
+ public void toLongStringOnSomeRealAddresses()
+ {
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:0001", fromString("::1").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0001:0000", fromString("::1:0").toLongString());
+ assertEquals("0001:0000:0000:0000:0001:0000:0000:0000", fromString("1::1:0:0:0").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:ffff", fromString("::ffff").toLongString());
+ assertEquals("ffff:0000:0000:0000:0000:0000:0000:0000", fromString("ffff::").toLongString());
+ assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toLongString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("2000::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(15)));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+ @Test
+ public void numberOfLeadingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfLeadingOnes());
+ assertEquals(1, fromString("8000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::f").numberOfLeadingOnes());
+ assertEquals(65, fromString("ffff:ffff:ffff:ffff:8000::f").numberOfLeadingOnes());
+ }
+
+ @Test
+ public void numberOfTrailingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfTrailingZeroes());
+ assertEquals(127, fromString("8000::").numberOfTrailingZeroes());
+ assertEquals(124, fromString("f000::").numberOfTrailingZeroes());
+ assertEquals(0, fromString("f000::f").numberOfTrailingZeroes());
+ assertEquals(63, fromString("ffff:ffff:ffff:ffff:8000::").numberOfTrailingZeroes());
+ }
+
+ @Test
+ public void numberOfLeadingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfLeadingZeroes());
+ assertEquals(0, fromString("8000::").numberOfLeadingZeroes());
+ assertEquals(124, fromString("::f").numberOfLeadingZeroes());
+ assertEquals(63, fromString("::1:ffff:ffff:ffff:ffff").numberOfLeadingZeroes());
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..960d630
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
new file mode 100644
index 0000000..0eea592
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkMaskTest
+{
+ @Test
+ public void constructValidNetworkMasks()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)), new IPv6NetworkMask(128));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)), new IPv6NetworkMask(127));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffcL)), new IPv6NetworkMask(126));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)), new IPv6NetworkMask(65));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)), new IPv6NetworkMask(64));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xc000000000000000L, 0x0L)), new IPv6NetworkMask(2));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)), new IPv6NetworkMask(1));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_Negative()
+ {
+ new IPv6NetworkMask(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_TooBig()
+ {
+ new IPv6NetworkMask(129);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromAddress()
+ {
+ IPv6NetworkMask.fromAddress(new IPv6Address(123L, 456L));
+ }
+
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..a40b1bb
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromTwoAddresses()
+ {
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)),
+ IPv6Network.fromTwoAddresses(fromString("::1"), fromString("::2")));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("a:b::"), IPv6NetworkMask.fromPrefixLength(44)),
+ IPv6Network.fromTwoAddresses(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)).toString());
+ assertEquals("a:b:c:d::/64", IPv6Network.fromAddressAndMask(fromString("a:b:c:d::"), IPv6NetworkMask.fromPrefixLength(64))
+ .toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = IPv6Network.fromAddressAndMask(new IPv6Address(rg.nextLong(), rg.nextLong()),
+ IPv6NetworkMask.fromPrefixLength(rg.nextInt(128) + 1));
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+ @Test
+ public void constructAndVerifyPrefixLength()
+ {
+ assertEquals(1, IPv6Network.fromString("a:b:c::/1").getNetmask().asPrefixLength());
+ assertEquals(63, IPv6Network.fromString("a:b:c::/63").getNetmask().asPrefixLength());
+ assertEquals(64, IPv6Network.fromString("a:b:c::/64").getNetmask().asPrefixLength());
+ assertEquals(65, IPv6Network.fromString("a:b:c::/65").getNetmask().asPrefixLength());
+ assertEquals(127, IPv6Network.fromString("a:b:c::/127").getNetmask().asPrefixLength());
+ assertEquals(128, IPv6Network.fromString("a:b:c::/128").getNetmask().asPrefixLength());
+ }
+
+ @Test
+ public void constructAndVerifyNetmask()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)),
+ IPv6Network.fromString("a:b:c::/1").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xfffffffffffffffeL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/63").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/64").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)),
+ IPv6Network.fromString("a:b:c::/65").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)),
+ IPv6Network.fromString("a:b:c::/127").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)),
+ IPv6Network.fromString("a:b:c::/128").getNetmask());
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6Network.fromString("ffff::/8").contains(IPv6Address.fromString("ffff::1")));
+ assertTrue(IPv6Network.fromString("1234:5678:1234:5678::/64").contains(IPv6Address.fromString("1234:5678:1234:5678:1::")));
+ }
+
+ @Test
+ public void zeroNetworkContainsEverything()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+
+ assertTrue(IPv6Network.fromString("::/0").contains(randomAddress));
+ assertTrue(IPv6Network.fromString("abcd:effe:dcba::/0").contains(randomAddress));
+ }
+}
diff --git a/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/examples/Examples.java b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/examples/Examples.java
new file mode 100644
index 0000000..380dc60
--- /dev/null
+++ b/tags/java-ipv6-0.10/src/test/java/com/googlecode/ipv6/examples/Examples.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6.examples;
+
+import com.googlecode.ipv6.*;
+import org.junit.Test;
+
+/**
+ * Some examples also featured in the online documentation. This class is in a separate package on purpose, such that we make sure only to
+ * call methods of the public API.
+ *
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void ipNetworkCalculation()
+ {
+ final IPv6Network strangeNetwork = IPv6Network.fromString("fe80::226:2dff:fefa:cd1f/43");
+
+ System.out.println(strangeNetwork.getFirst()); // prints fe80::
+ System.out.println(strangeNetwork.getLast()); // prints fe80:0:1f:ffff:ffff:ffff:ffff:ffff
+ System.out.println(strangeNetwork.getNetmask().asPrefixLength()); // prints 43
+ System.out.println(strangeNetwork.getNetmask().asAddress()); // prints ffff:ffff:ffe0::
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void ipNetworkMaskConstruction()
+ {
+ final IPv6NetworkMask slash40Network = IPv6NetworkMask.fromPrefixLength(40);
+ System.out.println(slash40Network.asAddress()); // prints ffff:ffff:ff00::
+ System.out.println(slash40Network.asPrefixLength()); // prints 40
+
+ final IPv6NetworkMask slash40NetworkConstructedFromAddressNotation = IPv6NetworkMask.fromAddress(
+ IPv6Address.fromString("ffff:ffff:ff00::"));
+ System.out.println(slash40Network.equals(slash40NetworkConstructedFromAddressNotation)); // prints true
+
+ final IPv6NetworkMask invalidNetworkMask = IPv6NetworkMask.fromAddress(IPv6Address.fromString("0fff::")); // fails
+ }
+
+ @Test
+ public void ipAddressNetworkMasking()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+
+ final IPv6Address masked = iPv6Address.maskWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(masked.toString()); // prints fe80::
+
+ final IPv6Address maximum = iPv6Address.maximumAddressWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(maximum.toString()); // prints fe80:0:ff:ffff:ffff:ffff:ffff:ffff
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(
+ IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff")),
+ IPv6NetworkMask.fromPrefixLength(120));
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/LICENSE b/tags/java-ipv6-0.11/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tags/java-ipv6-0.11/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/java-ipv6-0.11/NOTICE b/tags/java-ipv6-0.11/NOTICE
new file mode 100644
index 0000000..a4605c4
--- /dev/null
+++ b/tags/java-ipv6-0.11/NOTICE
@@ -0,0 +1,2 @@
+ Java IPv6
+ Copyright 2013 Jan Van Besien
\ No newline at end of file
diff --git a/tags/java-ipv6-0.11/pom.xml b/tags/java-ipv6-0.11/pom.xml
new file mode 100644
index 0000000..4102d46
--- /dev/null
+++ b/tags/java-ipv6-0.11/pom.xml
@@ -0,0 +1,143 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ 0.11
+ jar
+ Java IPv6 Library
+ http://code.google.com/p/java-ipv6
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.11
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.11
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.11
+
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+
+
+ ${basedir}
+ false
+
+ LICENSE
+ NOTICE
+
+
+
+
+
+
+
+ sonatype-oss-release
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 1.0-beta-4
+
+
+ validate
+
+ create
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/BitSetHelpers.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
new file mode 100644
index 0000000..698c51e
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * This class contains some helpers for working with BitSets. These are generally not necessary in JDK7, since the BitSet.valueOf(long[])
+ * method. However, for java-6 compatibility, we go this way.
+ *
+ * @author Jan Van Besien
+ */
+class BitSetHelpers
+{
+ static BitSet bitSetOf(long lowerBits, long upperBits)
+ {
+ final BitSet bitSet = new BitSet();
+ convert(lowerBits, 0, bitSet);
+ convert(upperBits, Long.SIZE, bitSet);
+ return bitSet;
+ }
+
+ static void convert(long value, int bitSetOffset, BitSet bits)
+ {
+ int index = 0;
+ while (value != 0L)
+ {
+ if (value % 2L != 0)
+ {
+ bits.set(bitSetOffset + index);
+ }
+ ++index;
+ value = value >>> 1;
+ }
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6Address.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6Address.java
new file mode 100644
index 0000000..8bd3788
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6Address.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.LongBuffer;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private static final int N_BYTES = 16;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ * IPv6Addresses from a {@link String} or an {@link java.net.InetAddress}. The internal representation of an IPv6Address is exactly
+ * these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
+ *
+ * @param highBits highest order bits
+ * @param lowBits lowest order bits
+ */
+ public static IPv6Address fromLongs(long highBits, long lowBits)
+ {
+ return new IPv6Address(highBits, lowBits);
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::".
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String longNotation = IPv6AddressHelpers.expandShortNotation(string);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return IPv6AddressHelpers.mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return IPv6AddressHelpers.parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Create an IPv6 address from a byte array.
+ *
+ * @param bytes byte array with 16 bytes
+ * @return IPv6 address
+ */
+ public static IPv6Address fromByteArray(final byte[] bytes)
+ {
+ if (bytes == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+ if (bytes.length != N_BYTES)
+ throw new IllegalArgumentException("the byte array to construct from should be 16 bytes long");
+
+ ByteBuffer buf = ByteBuffer.allocate(N_BYTES);
+ for (byte b : bytes)
+ {
+ buf.put(b);
+ }
+
+ buf.rewind();
+ LongBuffer longBuffer = buf.asLongBuffer();
+ return new IPv6Address(longBuffer.get(), longBuffer.get());
+ }
+
+ public byte[] toByteArray()
+ {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(N_BYTES).putLong(highBits).putLong(lowBits);
+ return byteBuffer.array();
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something positive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are zero
+ */
+ public IPv6Address maskWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (networkMask.asPrefixLength() == 0)
+ {
+ return new IPv6Address(0, 0);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - networkMask.asPrefixLength())), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are one
+ */
+ public IPv6Address maximumAddressWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> networkMask.asPrefixLength()), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ /**
+ * @return String representation of the IPv6 address, using shorthand notation whenever possible.
+ */
+ @Override
+ public String toString()
+ {
+ final String[] strings = toArrayOfShortStrings();
+
+ final StringBuilder result = new StringBuilder();
+
+ int[] shortHandNotationPositionAndLength = startAndLengthOfLongestRunOfZeroes();
+ int shortHandNotationPosition = shortHandNotationPositionAndLength[0];
+ int shortHandNotationLength = shortHandNotationPositionAndLength[1];
+
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (i == shortHandNotationPosition)
+ {
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(i > shortHandNotationPosition && i < shortHandNotationPosition + shortHandNotationLength))
+ {
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toArrayOfShortStrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%x", shorts[i]);
+ }
+ return strings;
+ }
+
+ /**
+ * @return String representation of the IPv6 address, never using shorthand notation.
+ */
+ public String toLongString()
+ {
+ final String[] strings = toArrayOfZeroPaddedstrings();
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < strings.length - 1; i++)
+ {
+ result.append(strings[i]).append(":");
+ }
+
+ result.append(strings[strings.length - 1]);
+
+ return result.toString();
+ }
+
+ private String[] toArrayOfZeroPaddedstrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%04x", shorts[i]);
+ }
+ return strings;
+ }
+
+ private short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ int[] startAndLengthOfLongestRunOfZeroes()
+ {
+ int longestConsecutiveZeroes = 0;
+ int longestConsecutiveZeroesPos = -1;
+ short[] shorts = toShortArray();
+ for (int pos = 0; pos < shorts.length; pos++)
+ {
+ int consecutiveZeroesAtCurrentPos = countConsecutiveZeroes(shorts, pos);
+ if (consecutiveZeroesAtCurrentPos > longestConsecutiveZeroes)
+ {
+ longestConsecutiveZeroes = consecutiveZeroesAtCurrentPos;
+ longestConsecutiveZeroesPos = pos;
+ }
+ }
+
+ return new int[]{longestConsecutiveZeroesPos, longestConsecutiveZeroes};
+ }
+
+ private int countConsecutiveZeroes(short[] shorts, int offset)
+ {
+ int count = 0;
+ for (int i = offset; i < shorts.length && shorts[i] == 0; i++)
+ {
+ count++;
+ }
+
+ return count;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ public int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ?
+ Long.numberOfTrailingZeros(highBits) + 64 :
+ Long.numberOfTrailingZeros(lowBits);
+ }
+
+ public int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+ public int numberOfLeadingZeroes()
+ {
+ return highBits == 0 ?
+ Long.numberOfLeadingZeros(lowBits) + 64 :
+ Long.numberOfLeadingZeros(highBits);
+ }
+
+ public int numberOfLeadingOnes()
+ {
+ // count leading ones in "value" by counting leading zeroes in "~ value"
+ final IPv6Address flipped = new IPv6Address(~this.highBits, ~this.lowBits);
+ return flipped.numberOfLeadingZeroes();
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..edf129a
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.Arrays;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isZeroString(String string)
+ {
+ return "0".equals(string);
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..77b9a0f
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+
+import java.util.*;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final IPv6NetworkMask allocationSubnetSize;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ * size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ * (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ */
+ public static IPv6AddressPool fromRangeAndSubnet(final IPv6AddressRange range,
+ final IPv6NetworkMask allocationSubnetSize)
+ {
+ // in the beginning, all is free
+ return new IPv6AddressPool(range, allocationSubnetSize, new TreeSet(Arrays.asList(range)), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6AddressRange range, final IPv6NetworkMask allocationSubnetSize,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = range;
+
+ this.allocationSubnetSize = allocationSubnetSize;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(underlyingRange, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenSize(underlyingRange, allocationSubnetSize);
+ }
+
+ private void validateFreeRanges(IPv6AddressRange range, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(range, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be useful. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6AddressRange range, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(range.getFirst()) >= 0
+ && toValidate.last().getLast().compareTo(range.getLast()) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenSize(IPv6AddressRange range, IPv6NetworkMask allocationSubnetSize)
+ {
+ final int allocatableBits = 128 - allocationSubnetSize.asPrefixLength();
+
+ if (range.getFirst().numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength() + "], "
+ + "first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (range.getLast().numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength()
+ + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = IPv6Network.fromAddressAndMask(firstFreeRange.getFirst(), allocationSubnetSize);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (!this.allocationSubnetSize.equals(toAllocate.getNetmask()))
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getNetmask().asPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /"
+ + allocationSubnetSize);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (!this.allocationSubnetSize.equals(network.getNetmask()))
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getNetmask().asPrefixLength()
+ + "] can not be free in a pool which uses prefix length [" +
+ allocationSubnetSize + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (allocationSubnetSize != null ? !allocationSubnetSize.equals(that.allocationSubnetSize) : that.allocationSubnetSize != null)
+ return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + (allocationSubnetSize != null ? allocationSubnetSize.hashCode() : 0);
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return underlyingRange.toLongString();
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..309335e
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.*;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public static IPv6AddressRange fromFirstAndLast(IPv6Address first, IPv6Address last)
+ {
+ return new IPv6AddressRange(first, last);
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(fromFirstAndLast(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(fromFirstAndLast(first, last.subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, address.subtract(1)),
+ fromFirstAndLast(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return fromFirstAndLast(address, last);
+ else if (address.compareTo(last) > 0)
+ return fromFirstAndLast(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(fromFirstAndLast(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(fromFirstAndLast(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, network.getFirst().subtract(1)),
+ fromFirstAndLast(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return first.toLongString() + " - " + last.toLongString();
+ }
+
+ /**
+ * The natural order of {@link com.googlecode.ipv6.IPv6AddressRange}s orders them on increasing first addresses, and on increasing last
+ * address if the first address would be equal.
+ *
+ * Note that the natural order does thus not compare sizes of ranges.
+ *
+ * @param that range to compare with
+ * @return negative, zero or positive depending on whether this is smaller, equal or greater than that
+ */
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (!this.first.equals(that.first))
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ return current = current.add(1);
+ else
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6Network.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6Network.java
new file mode 100644
index 0000000..60aa407
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6Network.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final IPv6NetworkMask networkMask;
+
+ /**
+ * Construct from address and network mask.
+ *
+ * @param address address
+ * @param networkMask network mask
+ */
+ private IPv6Network(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ super(address.maskWithNetworkMask(networkMask), address.maximumAddressWithNetworkMask(networkMask));
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Create an IPv6 network from an IPv6Address and an IPv6NetworkMask
+ *
+ * @param address IPv6 address (the network address or any other address within the network)
+ * @param networkMask IPv6 network mask
+ * @return IPv6 network
+ */
+ public static IPv6Network fromAddressAndMask(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ return new IPv6Network(address, networkMask);
+ }
+
+ /**
+ * Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ * length") which contains both addresses.
+ *
+ * @param one address one
+ * @param two address two, should be bigger than address one
+ */
+ public static IPv6Network fromTwoAddresses(IPv6Address one, IPv6Address two)
+ {
+ final IPv6NetworkMask longestPrefixLength = IPv6NetworkMask.fromPrefixLength(IPv6NetworkHelpers.longestPrefixLength(one, two));
+ return new IPv6Network(one.maskWithNetworkMask(longestPrefixLength), longestPrefixLength);
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 network
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return fromAddressAndMask(networkAddress, new IPv6NetworkMask(prefixLength));
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + networkMask.asPrefixLength();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return address.toLongString() + "/" + networkMask.asPrefixLength();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+ if (networkMask != null ? !networkMask.equals(that.networkMask) : that.networkMask != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = super.hashCode();
+ result = 31 * result + (address != null ? address.hashCode() : 0);
+ result = 31 * result + (networkMask != null ? networkMask.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6NetworkMask getNetmask()
+ {
+ return networkMask;
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..66268ed
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = bitSetOf(first.getLowBits(), first.getHighBits());
+ final BitSet lastBits = bitSetOf(last.getLowBits(), last.getHighBits());
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet a, BitSet b)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (a.get(i) == b.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
new file mode 100644
index 0000000..ee4394b
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ * starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6NetworkMask
+{
+ private final int prefixLength;
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ IPv6NetworkMask(int prefixLength)
+ {
+ if (prefixLength < 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval [0, 128]");
+
+ this.prefixLength = prefixLength;
+ }
+
+
+ /**
+ * Construct an IPv6 network mask from an IPv6 address. The address should be a valid network mask.
+ *
+ * @param iPv6Address address to use as network mask
+ * @throws IllegalArgumentException if the address is not a valid network mask
+ */
+ public static IPv6NetworkMask fromAddress(final IPv6Address iPv6Address)
+ {
+ validateNetworkMask(iPv6Address);
+ return new IPv6NetworkMask(iPv6Address.numberOfLeadingOnes());
+ }
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ public static IPv6NetworkMask fromPrefixLength(int prefixLength)
+ {
+ return new IPv6NetworkMask(prefixLength);
+ }
+
+ private static void validateNetworkMask(IPv6Address addressToValidate)
+ {
+ final BitSet addressAsBitSet = bitSetOf(addressToValidate.getLowBits(), addressToValidate.getHighBits());
+ if (!addressAsBitSet.get(127))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ else
+ {
+ boolean firstZeroFound = false;
+ for (int i = 127; i >= 0 && !firstZeroFound; i--)
+ {
+ if (!addressAsBitSet.get(i))
+ {
+ firstZeroFound = true;
+
+ // a zero -> all the others should also be zero
+ for (int j = i - 1; j >= 0; j--)
+ {
+ if (addressAsBitSet.get(j))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int asPrefixLength()
+ {
+ return prefixLength;
+ }
+
+ public IPv6Address asAddress()
+ {
+ if (prefixLength == 128)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0L);
+ }
+ else if (prefixLength > 64)
+ {
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL << (64 - prefixLength), 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6NetworkMask that = (IPv6NetworkMask) o;
+
+ if (prefixLength != that.prefixLength) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefixLength;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "" + prefixLength;
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..834d561
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::1"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:fffe")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::ab00"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("2001::ffff:ffff")), new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::abcd:ef00"),
+ fromString("2001::abcd:efff")), new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null",
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::99"), IPv6NetworkMask.fromPrefixLength(128)));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(66));
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..e699741
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ IPv6AddressRange.fromFirstAndLast(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8"))
+ .size());
+ assertEquals(0, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4"))
+ .size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+ @Test
+ public void compareTo()
+ {
+ final IPv6AddressRange a =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange b =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange c =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange d =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+
+ Assert.assertTrue(a.compareTo(b) > 0);
+ Assert.assertTrue(a.compareTo(c) < 0);
+ Assert.assertTrue(a.compareTo(d) < 0);
+ Assert.assertTrue(b.compareTo(c) < 0);
+ Assert.assertTrue(b.compareTo(d) < 0);
+ Assert.assertTrue(c.compareTo(d) > 0);
+
+ Assert.assertTrue(a.compareTo(a) == 0);
+ Assert.assertTrue(b.compareTo(b) == 0);
+ Assert.assertTrue(c.compareTo(c) == 0);
+ Assert.assertTrue(d.compareTo(d) == 0);
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..38c8a2e
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromInetAddress;
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test
+ public void toLongStringOnSomeRealAddresses()
+ {
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:0001", fromString("::1").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0001:0000", fromString("::1:0").toLongString());
+ assertEquals("0001:0000:0000:0000:0001:0000:0000:0000", fromString("1::1:0:0:0").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:ffff", fromString("::ffff").toLongString());
+ assertEquals("ffff:0000:0000:0000:0000:0000:0000:0000", fromString("ffff::").toLongString());
+ assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toLongString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void constructFromByteArray() throws UnknownHostException
+ {
+ assertEquals("1:1:1:1:1:1:1:1",
+ IPv6Address.fromByteArray(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01})
+ .toString());
+ }
+
+ @Test
+ public void convertToByteArray() throws UnknownHostException
+ {
+ assertArrayEquals(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01},
+ IPv6Address.fromString("1:1:1:1:1:1:1:1").toByteArray());
+ }
+
+ @Test
+ public void convertToAndFromByteArray()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ byte[] randomBytes = new byte[16];
+ rg.nextBytes(randomBytes);
+
+ final IPv6Address address = IPv6Address.fromByteArray(randomBytes);
+ assertArrayEquals(randomBytes, address.toByteArray());
+ }
+ }
+
+ @Test
+ public void positionOfLongestRunOfZeroes()
+ {
+ assertArrayEquals(new int[]{0, 8}, fromString("::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{3, 5}, fromString("a:b:c::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{2, 5}, fromString("a:b::c").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{4, 4}, fromString("a:0:0:c::").startAndLengthOfLongestRunOfZeroes());
+ }
+
+ @Test
+ public void toStringCompactsLongestRunOfZeroes()
+ {
+ assertEquals("0:0:1::", fromString("0:0:1::").toString()); // and not ::1:0:0:0:0:0
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("2000::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(15)));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+ @Test
+ public void numberOfLeadingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfLeadingOnes());
+ assertEquals(1, fromString("8000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::f").numberOfLeadingOnes());
+ assertEquals(65, fromString("ffff:ffff:ffff:ffff:8000::f").numberOfLeadingOnes());
+ }
+
+ @Test
+ public void numberOfTrailingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfTrailingZeroes());
+ assertEquals(127, fromString("8000::").numberOfTrailingZeroes());
+ assertEquals(124, fromString("f000::").numberOfTrailingZeroes());
+ assertEquals(0, fromString("f000::f").numberOfTrailingZeroes());
+ assertEquals(63, fromString("ffff:ffff:ffff:ffff:8000::").numberOfTrailingZeroes());
+ }
+
+ @Test
+ public void numberOfLeadingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfLeadingZeroes());
+ assertEquals(0, fromString("8000::").numberOfLeadingZeroes());
+ assertEquals(124, fromString("::f").numberOfLeadingZeroes());
+ assertEquals(63, fromString("::1:ffff:ffff:ffff:ffff").numberOfLeadingZeroes());
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..960d630
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
new file mode 100644
index 0000000..0eea592
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkMaskTest
+{
+ @Test
+ public void constructValidNetworkMasks()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)), new IPv6NetworkMask(128));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)), new IPv6NetworkMask(127));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffcL)), new IPv6NetworkMask(126));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)), new IPv6NetworkMask(65));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)), new IPv6NetworkMask(64));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xc000000000000000L, 0x0L)), new IPv6NetworkMask(2));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)), new IPv6NetworkMask(1));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_Negative()
+ {
+ new IPv6NetworkMask(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_TooBig()
+ {
+ new IPv6NetworkMask(129);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromAddress()
+ {
+ IPv6NetworkMask.fromAddress(new IPv6Address(123L, 456L));
+ }
+
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..a40b1bb
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromTwoAddresses()
+ {
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)),
+ IPv6Network.fromTwoAddresses(fromString("::1"), fromString("::2")));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("a:b::"), IPv6NetworkMask.fromPrefixLength(44)),
+ IPv6Network.fromTwoAddresses(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)).toString());
+ assertEquals("a:b:c:d::/64", IPv6Network.fromAddressAndMask(fromString("a:b:c:d::"), IPv6NetworkMask.fromPrefixLength(64))
+ .toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = IPv6Network.fromAddressAndMask(new IPv6Address(rg.nextLong(), rg.nextLong()),
+ IPv6NetworkMask.fromPrefixLength(rg.nextInt(128) + 1));
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+ @Test
+ public void constructAndVerifyPrefixLength()
+ {
+ assertEquals(1, IPv6Network.fromString("a:b:c::/1").getNetmask().asPrefixLength());
+ assertEquals(63, IPv6Network.fromString("a:b:c::/63").getNetmask().asPrefixLength());
+ assertEquals(64, IPv6Network.fromString("a:b:c::/64").getNetmask().asPrefixLength());
+ assertEquals(65, IPv6Network.fromString("a:b:c::/65").getNetmask().asPrefixLength());
+ assertEquals(127, IPv6Network.fromString("a:b:c::/127").getNetmask().asPrefixLength());
+ assertEquals(128, IPv6Network.fromString("a:b:c::/128").getNetmask().asPrefixLength());
+ }
+
+ @Test
+ public void constructAndVerifyNetmask()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)),
+ IPv6Network.fromString("a:b:c::/1").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xfffffffffffffffeL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/63").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/64").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)),
+ IPv6Network.fromString("a:b:c::/65").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)),
+ IPv6Network.fromString("a:b:c::/127").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)),
+ IPv6Network.fromString("a:b:c::/128").getNetmask());
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6Network.fromString("ffff::/8").contains(IPv6Address.fromString("ffff::1")));
+ assertTrue(IPv6Network.fromString("1234:5678:1234:5678::/64").contains(IPv6Address.fromString("1234:5678:1234:5678:1::")));
+ }
+
+ @Test
+ public void zeroNetworkContainsEverything()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+
+ assertTrue(IPv6Network.fromString("::/0").contains(randomAddress));
+ assertTrue(IPv6Network.fromString("abcd:effe:dcba::/0").contains(randomAddress));
+ }
+}
diff --git a/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/examples/Examples.java b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/examples/Examples.java
new file mode 100644
index 0000000..380dc60
--- /dev/null
+++ b/tags/java-ipv6-0.11/src/test/java/com/googlecode/ipv6/examples/Examples.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6.examples;
+
+import com.googlecode.ipv6.*;
+import org.junit.Test;
+
+/**
+ * Some examples also featured in the online documentation. This class is in a separate package on purpose, such that we make sure only to
+ * call methods of the public API.
+ *
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void ipNetworkCalculation()
+ {
+ final IPv6Network strangeNetwork = IPv6Network.fromString("fe80::226:2dff:fefa:cd1f/43");
+
+ System.out.println(strangeNetwork.getFirst()); // prints fe80::
+ System.out.println(strangeNetwork.getLast()); // prints fe80:0:1f:ffff:ffff:ffff:ffff:ffff
+ System.out.println(strangeNetwork.getNetmask().asPrefixLength()); // prints 43
+ System.out.println(strangeNetwork.getNetmask().asAddress()); // prints ffff:ffff:ffe0::
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void ipNetworkMaskConstruction()
+ {
+ final IPv6NetworkMask slash40Network = IPv6NetworkMask.fromPrefixLength(40);
+ System.out.println(slash40Network.asAddress()); // prints ffff:ffff:ff00::
+ System.out.println(slash40Network.asPrefixLength()); // prints 40
+
+ final IPv6NetworkMask slash40NetworkConstructedFromAddressNotation = IPv6NetworkMask.fromAddress(
+ IPv6Address.fromString("ffff:ffff:ff00::"));
+ System.out.println(slash40Network.equals(slash40NetworkConstructedFromAddressNotation)); // prints true
+
+ final IPv6NetworkMask invalidNetworkMask = IPv6NetworkMask.fromAddress(IPv6Address.fromString("0fff::")); // fails
+ }
+
+ @Test
+ public void ipAddressNetworkMasking()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+
+ final IPv6Address masked = iPv6Address.maskWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(masked.toString()); // prints fe80::
+
+ final IPv6Address maximum = iPv6Address.maximumAddressWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(maximum.toString()); // prints fe80:0:ff:ffff:ffff:ffff:ffff:ffff
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(
+ IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff")),
+ IPv6NetworkMask.fromPrefixLength(120));
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/LICENSE b/tags/java-ipv6-0.12/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tags/java-ipv6-0.12/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/java-ipv6-0.12/NOTICE b/tags/java-ipv6-0.12/NOTICE
new file mode 100644
index 0000000..a4605c4
--- /dev/null
+++ b/tags/java-ipv6-0.12/NOTICE
@@ -0,0 +1,2 @@
+ Java IPv6
+ Copyright 2013 Jan Van Besien
\ No newline at end of file
diff --git a/tags/java-ipv6-0.12/pom.xml b/tags/java-ipv6-0.12/pom.xml
new file mode 100644
index 0000000..75028ef
--- /dev/null
+++ b/tags/java-ipv6-0.12/pom.xml
@@ -0,0 +1,143 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ 0.12
+ jar
+ Java IPv6 Library
+ http://code.google.com/p/java-ipv6
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.12
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.12
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.12
+
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+
+
+ ${basedir}
+ false
+
+ LICENSE
+ NOTICE
+
+
+
+
+
+
+
+ sonatype-oss-release
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 1.0-beta-4
+
+
+ validate
+
+ create
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/BitSetHelpers.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
new file mode 100644
index 0000000..698c51e
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * This class contains some helpers for working with BitSets. These are generally not necessary in JDK7, since the BitSet.valueOf(long[])
+ * method. However, for java-6 compatibility, we go this way.
+ *
+ * @author Jan Van Besien
+ */
+class BitSetHelpers
+{
+ static BitSet bitSetOf(long lowerBits, long upperBits)
+ {
+ final BitSet bitSet = new BitSet();
+ convert(lowerBits, 0, bitSet);
+ convert(upperBits, Long.SIZE, bitSet);
+ return bitSet;
+ }
+
+ static void convert(long value, int bitSetOffset, BitSet bits)
+ {
+ int index = 0;
+ while (value != 0L)
+ {
+ if (value % 2L != 0)
+ {
+ bits.set(bitSetOffset + index);
+ }
+ ++index;
+ value = value >>> 1;
+ }
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6Address.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6Address.java
new file mode 100644
index 0000000..056114c
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6Address.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.LongBuffer;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private static final int N_BYTES = 16;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ * IPv6Addresses from a {@link String} or an {@link java.net.InetAddress}. The internal representation of an IPv6Address is exactly
+ * these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
+ *
+ * @param highBits highest order bits
+ * @param lowBits lowest order bits
+ */
+ public static IPv6Address fromLongs(long highBits, long lowBits)
+ {
+ return new IPv6Address(highBits, lowBits);
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String withoutIPv4MappedNotation = IPv6AddressHelpers.rewriteIPv4MappedNotation(string);
+ final String longNotation = IPv6AddressHelpers.expandShortNotation(withoutIPv4MappedNotation);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return IPv6AddressHelpers.mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return IPv6AddressHelpers.parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Create an IPv6 address from a byte array.
+ *
+ * @param bytes byte array with 16 bytes
+ * @return IPv6 address
+ */
+ public static IPv6Address fromByteArray(final byte[] bytes)
+ {
+ if (bytes == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+ if (bytes.length != N_BYTES)
+ throw new IllegalArgumentException("the byte array to construct from should be 16 bytes long");
+
+ ByteBuffer buf = ByteBuffer.allocate(N_BYTES);
+ for (byte b : bytes)
+ {
+ buf.put(b);
+ }
+
+ buf.rewind();
+ LongBuffer longBuffer = buf.asLongBuffer();
+ return new IPv6Address(longBuffer.get(), longBuffer.get());
+ }
+
+ public byte[] toByteArray()
+ {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(N_BYTES).putLong(highBits).putLong(lowBits);
+ return byteBuffer.array();
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something positive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are zero
+ */
+ public IPv6Address maskWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (networkMask.asPrefixLength() == 0)
+ {
+ return new IPv6Address(0, 0);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - networkMask.asPrefixLength())), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are one
+ */
+ public IPv6Address maximumAddressWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> networkMask.asPrefixLength()), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ /**
+ * Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ * and the remaining 32 bits are the IPv4 address.
+ *
+ * @return true if the address is an IPv4-mapped IPv6 addresses.
+ */
+ public boolean isIPv4Mapped()
+ {
+ return this.highBits == 0 // 64 zero bits
+ && (this.lowBits & 0xFFFF000000000000L) == 0 // 16 more zero bits
+ && (this.lowBits & 0x0000FFFF00000000L) == 0x0000FFFF00000000L; // 16 one bits and the remainder is the IPv4 address
+ }
+
+ /**
+ * @return true if the address is an IPv6 multicast address (an address in the network ff00::/8)
+ */
+ public boolean isMulticast()
+ {
+ return IPv6Network.fromString("ff00::/8").contains(this);
+ }
+
+ /**
+ * @return true if the address is an IPv6 site-local address (an address in the network fec0::/48)
+ */
+ public boolean isSiteLocal()
+ {
+ return IPv6Network.fromString("fec0::/48").contains(this);
+ }
+
+ /**
+ * @return true if the address is an IPv6 link-local address (an address in the network fe80::/48)
+ */
+ public boolean isLinkLocal()
+ {
+ return IPv6Network.fromString("fe80::/64").contains(this);
+ }
+
+ /**
+ * Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ * addresses whenever possible.
+ *
+ * @return String representation of the IPv6 address
+ */
+ @Override
+ public String toString()
+ {
+ if (isIPv4Mapped())
+ return toIPv4MappedAddressString();
+ else
+ return toShortHandNotationString();
+ }
+
+ private String toIPv4MappedAddressString()
+ {
+ int byteZero = (int) ((this.lowBits & 0x00000000FF000000L) >> 24);
+ int byteOne = (int) ((this.lowBits & 0x0000000000FF0000L) >> 16);
+ int byteTwo = (int) ((this.lowBits & 0x000000000000FF00L) >> 8);
+ int byteThree = (int) ((this.lowBits & 0x00000000000000FFL));
+
+ final StringBuilder result = new StringBuilder("::ffff:");
+ result.append(byteZero).append(".").append(byteOne).append(".").append(byteTwo).append(".").append(byteThree);
+
+ return result.toString();
+ }
+
+ private String toShortHandNotationString()
+ {
+ final String[] strings = toArrayOfShortStrings();
+
+ final StringBuilder result = new StringBuilder();
+
+ int[] shortHandNotationPositionAndLength = startAndLengthOfLongestRunOfZeroes();
+ int shortHandNotationPosition = shortHandNotationPositionAndLength[0];
+ int shortHandNotationLength = shortHandNotationPositionAndLength[1];
+
+ boolean useShortHandNotation = shortHandNotationLength > 1; // RFC5952 recommends not to use shorthand notation for a single zero
+
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (useShortHandNotation && i == shortHandNotationPosition)
+ {
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(i > shortHandNotationPosition && i < shortHandNotationPosition + shortHandNotationLength))
+ {
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toArrayOfShortStrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%x", shorts[i]);
+ }
+ return strings;
+ }
+
+ /**
+ * @return String representation of the IPv6 address, never using shorthand notation.
+ */
+ public String toLongString()
+ {
+ final String[] strings = toArrayOfZeroPaddedstrings();
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < strings.length - 1; i++)
+ {
+ result.append(strings[i]).append(":");
+ }
+
+ result.append(strings[strings.length - 1]);
+
+ return result.toString();
+ }
+
+ private String[] toArrayOfZeroPaddedstrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%04x", shorts[i]);
+ }
+ return strings;
+ }
+
+ private short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ int[] startAndLengthOfLongestRunOfZeroes()
+ {
+ int longestConsecutiveZeroes = 0;
+ int longestConsecutiveZeroesPos = -1;
+ short[] shorts = toShortArray();
+ for (int pos = 0; pos < shorts.length; pos++)
+ {
+ int consecutiveZeroesAtCurrentPos = countConsecutiveZeroes(shorts, pos);
+ if (consecutiveZeroesAtCurrentPos > longestConsecutiveZeroes)
+ {
+ longestConsecutiveZeroes = consecutiveZeroesAtCurrentPos;
+ longestConsecutiveZeroesPos = pos;
+ }
+ }
+
+ return new int[]{longestConsecutiveZeroesPos, longestConsecutiveZeroes};
+ }
+
+ private int countConsecutiveZeroes(short[] shorts, int offset)
+ {
+ int count = 0;
+ for (int i = offset; i < shorts.length && shorts[i] == 0; i++)
+ {
+ count++;
+ }
+
+ return count;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ public int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ?
+ Long.numberOfTrailingZeros(highBits) + 64 :
+ Long.numberOfTrailingZeros(lowBits);
+ }
+
+ public int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+ public int numberOfLeadingZeroes()
+ {
+ return highBits == 0 ?
+ Long.numberOfLeadingZeros(lowBits) + 64 :
+ Long.numberOfLeadingZeros(highBits);
+ }
+
+ public int numberOfLeadingOnes()
+ {
+ // count leading ones in "value" by counting leading zeroes in "~ value"
+ final IPv6Address flipped = new IPv6Address(~this.highBits, ~this.lowBits);
+ return flipped.numberOfLeadingZeroes();
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..1e4784f
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ private static final Pattern DOT_DELIM = Pattern.compile("\\.");
+
+ /**
+ * Replaces a w.x.y.z substring at the end of the given string with corresponding hexadecimal notation. This is useful in case the
+ * string was using IPv4-Mapped address notation.
+ */
+ static String rewriteIPv4MappedNotation(String string)
+ {
+ if (!string.contains("."))
+ {
+ return string;
+ }
+ else
+ {
+ int lastColon = string.lastIndexOf(":");
+ String firstPart = string.substring(0, lastColon + 1);
+ String mappedIPv4Part = string.substring(lastColon + 1);
+
+ if (mappedIPv4Part.contains("."))
+ {
+ String[] dotSplits = DOT_DELIM.split(mappedIPv4Part);
+ if (dotSplits.length != 4)
+ throw new IllegalArgumentException(String.format("can not parse [%s]", string));
+
+ StringBuilder rewrittenString = new StringBuilder();
+ rewrittenString.append(firstPart);
+ int byteZero = Integer.parseInt(dotSplits[0]);
+ int byteOne = Integer.parseInt(dotSplits[1]);
+ int byteTwo = Integer.parseInt(dotSplits[2]);
+ int byteThree = Integer.parseInt(dotSplits[3]);
+
+ rewrittenString.append(String.format("%02x", byteZero));
+ rewrittenString.append(String.format("%02x", byteOne));
+ rewrittenString.append(":");
+ rewrittenString.append(String.format("%02x", byteTwo));
+ rewrittenString.append(String.format("%02x", byteThree));
+
+ return rewrittenString.toString();
+ }
+ else
+ {
+ throw new IllegalArgumentException(String.format("can not parse [%s]", string));
+ }
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..77b9a0f
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+
+import java.util.*;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final IPv6NetworkMask allocationSubnetSize;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ * size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ * (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ */
+ public static IPv6AddressPool fromRangeAndSubnet(final IPv6AddressRange range,
+ final IPv6NetworkMask allocationSubnetSize)
+ {
+ // in the beginning, all is free
+ return new IPv6AddressPool(range, allocationSubnetSize, new TreeSet(Arrays.asList(range)), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6AddressRange range, final IPv6NetworkMask allocationSubnetSize,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = range;
+
+ this.allocationSubnetSize = allocationSubnetSize;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(underlyingRange, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenSize(underlyingRange, allocationSubnetSize);
+ }
+
+ private void validateFreeRanges(IPv6AddressRange range, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(range, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be useful. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6AddressRange range, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(range.getFirst()) >= 0
+ && toValidate.last().getLast().compareTo(range.getLast()) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenSize(IPv6AddressRange range, IPv6NetworkMask allocationSubnetSize)
+ {
+ final int allocatableBits = 128 - allocationSubnetSize.asPrefixLength();
+
+ if (range.getFirst().numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength() + "], "
+ + "first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (range.getLast().numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength()
+ + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = IPv6Network.fromAddressAndMask(firstFreeRange.getFirst(), allocationSubnetSize);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (!this.allocationSubnetSize.equals(toAllocate.getNetmask()))
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getNetmask().asPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /"
+ + allocationSubnetSize);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (!this.allocationSubnetSize.equals(network.getNetmask()))
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getNetmask().asPrefixLength()
+ + "] can not be free in a pool which uses prefix length [" +
+ allocationSubnetSize + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (allocationSubnetSize != null ? !allocationSubnetSize.equals(that.allocationSubnetSize) : that.allocationSubnetSize != null)
+ return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + (allocationSubnetSize != null ? allocationSubnetSize.hashCode() : 0);
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return underlyingRange.toLongString();
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..309335e
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.*;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public static IPv6AddressRange fromFirstAndLast(IPv6Address first, IPv6Address last)
+ {
+ return new IPv6AddressRange(first, last);
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(fromFirstAndLast(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(fromFirstAndLast(first, last.subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, address.subtract(1)),
+ fromFirstAndLast(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return fromFirstAndLast(address, last);
+ else if (address.compareTo(last) > 0)
+ return fromFirstAndLast(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(fromFirstAndLast(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(fromFirstAndLast(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, network.getFirst().subtract(1)),
+ fromFirstAndLast(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return first.toLongString() + " - " + last.toLongString();
+ }
+
+ /**
+ * The natural order of {@link com.googlecode.ipv6.IPv6AddressRange}s orders them on increasing first addresses, and on increasing last
+ * address if the first address would be equal.
+ *
+ * Note that the natural order does thus not compare sizes of ranges.
+ *
+ * @param that range to compare with
+ * @return negative, zero or positive depending on whether this is smaller, equal or greater than that
+ */
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (!this.first.equals(that.first))
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ return current = current.add(1);
+ else
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6Network.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6Network.java
new file mode 100644
index 0000000..60aa407
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6Network.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ private final IPv6Address address;
+
+ private final IPv6NetworkMask networkMask;
+
+ /**
+ * Construct from address and network mask.
+ *
+ * @param address address
+ * @param networkMask network mask
+ */
+ private IPv6Network(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ super(address.maskWithNetworkMask(networkMask), address.maximumAddressWithNetworkMask(networkMask));
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Create an IPv6 network from an IPv6Address and an IPv6NetworkMask
+ *
+ * @param address IPv6 address (the network address or any other address within the network)
+ * @param networkMask IPv6 network mask
+ * @return IPv6 network
+ */
+ public static IPv6Network fromAddressAndMask(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ return new IPv6Network(address, networkMask);
+ }
+
+ /**
+ * Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ * length") which contains both addresses.
+ *
+ * @param one address one
+ * @param two address two, should be bigger than address one
+ */
+ public static IPv6Network fromTwoAddresses(IPv6Address one, IPv6Address two)
+ {
+ final IPv6NetworkMask longestPrefixLength = IPv6NetworkMask.fromPrefixLength(IPv6NetworkHelpers.longestPrefixLength(one, two));
+ return new IPv6Network(one.maskWithNetworkMask(longestPrefixLength), longestPrefixLength);
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 network
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return fromAddressAndMask(networkAddress, new IPv6NetworkMask(prefixLength));
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + networkMask.asPrefixLength();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return address.toLongString() + "/" + networkMask.asPrefixLength();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+ if (networkMask != null ? !networkMask.equals(that.networkMask) : that.networkMask != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = super.hashCode();
+ result = 31 * result + (address != null ? address.hashCode() : 0);
+ result = 31 * result + (networkMask != null ? networkMask.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6NetworkMask getNetmask()
+ {
+ return networkMask;
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..66268ed
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = bitSetOf(first.getLowBits(), first.getHighBits());
+ final BitSet lastBits = bitSetOf(last.getLowBits(), last.getHighBits());
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet a, BitSet b)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (a.get(i) == b.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
new file mode 100644
index 0000000..ee4394b
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ * starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6NetworkMask
+{
+ private final int prefixLength;
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ IPv6NetworkMask(int prefixLength)
+ {
+ if (prefixLength < 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval [0, 128]");
+
+ this.prefixLength = prefixLength;
+ }
+
+
+ /**
+ * Construct an IPv6 network mask from an IPv6 address. The address should be a valid network mask.
+ *
+ * @param iPv6Address address to use as network mask
+ * @throws IllegalArgumentException if the address is not a valid network mask
+ */
+ public static IPv6NetworkMask fromAddress(final IPv6Address iPv6Address)
+ {
+ validateNetworkMask(iPv6Address);
+ return new IPv6NetworkMask(iPv6Address.numberOfLeadingOnes());
+ }
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ public static IPv6NetworkMask fromPrefixLength(int prefixLength)
+ {
+ return new IPv6NetworkMask(prefixLength);
+ }
+
+ private static void validateNetworkMask(IPv6Address addressToValidate)
+ {
+ final BitSet addressAsBitSet = bitSetOf(addressToValidate.getLowBits(), addressToValidate.getHighBits());
+ if (!addressAsBitSet.get(127))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ else
+ {
+ boolean firstZeroFound = false;
+ for (int i = 127; i >= 0 && !firstZeroFound; i--)
+ {
+ if (!addressAsBitSet.get(i))
+ {
+ firstZeroFound = true;
+
+ // a zero -> all the others should also be zero
+ for (int j = i - 1; j >= 0; j--)
+ {
+ if (addressAsBitSet.get(j))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int asPrefixLength()
+ {
+ return prefixLength;
+ }
+
+ public IPv6Address asAddress()
+ {
+ if (prefixLength == 128)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0L);
+ }
+ else if (prefixLength > 64)
+ {
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL << (64 - prefixLength), 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6NetworkMask that = (IPv6NetworkMask) o;
+
+ if (prefixLength != that.prefixLength) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefixLength;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "" + prefixLength;
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..834d561
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::1"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:fffe")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::ab00"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("2001::ffff:ffff")), new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::abcd:ef00"),
+ fromString("2001::abcd:efff")), new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null",
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::99"), IPv6NetworkMask.fromPrefixLength(128)));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(66));
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..e699741
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ IPv6AddressRange.fromFirstAndLast(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8"))
+ .size());
+ assertEquals(0, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4"))
+ .size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+ @Test
+ public void compareTo()
+ {
+ final IPv6AddressRange a =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange b =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange c =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange d =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+
+ Assert.assertTrue(a.compareTo(b) > 0);
+ Assert.assertTrue(a.compareTo(c) < 0);
+ Assert.assertTrue(a.compareTo(d) < 0);
+ Assert.assertTrue(b.compareTo(c) < 0);
+ Assert.assertTrue(b.compareTo(d) < 0);
+ Assert.assertTrue(c.compareTo(d) > 0);
+
+ Assert.assertTrue(a.compareTo(a) == 0);
+ Assert.assertTrue(b.compareTo(b) == 0);
+ Assert.assertTrue(c.compareTo(c) == 0);
+ Assert.assertTrue(d.compareTo(d) == 0);
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..3536ec3
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromInetAddress;
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesFromRFC5952()
+ {
+ assertEquals("::", fromString("::").toString());
+ assertEquals("1:2:3:4::", fromString("1:2:3:4::").toString());
+ assertEquals("::1:2:3:4", fromString("::1:2:3:4").toString());
+ assertEquals("1::2", fromString("1::2").toString());
+ assertEquals("::2", fromString("::2").toString());
+ assertEquals("1::", fromString("1::").toString());
+ assertEquals("a31:200:3abc::de4", fromString("0a31:0200:3AbC::0dE4").toString());
+ assertEquals("1::4:0:0:0", fromString("1:0:0:0:4:0:0:0").toString());
+
+ assertEquals("2001:db8::1", fromString("2001:db8::1").toString());
+ assertEquals("2001:db8::2:1", fromString("2001:db8:0:0:0:0:2:1").toString());
+ assertEquals("2001:db8:0:1:1:1:1:1", fromString("2001:db8:0:1:1:1:1:1").toString());
+ assertEquals("2001:db8::1:0:0:1", fromString("2001:db8::1:0:0:1").toString());
+ assertEquals("2001:0:0:1::1", fromString("2001:0:0:1:0:0:0:1").toString());
+
+ assertEquals("1:0:0:4::", fromString("1:0:0:4::").toString());
+ }
+
+ @Test
+ public void parseSomeRealIPv4MappedAddresses()
+ {
+ assertEquals("::ffff:0.0.0.1", fromString("::ffff:0.0.0.1").toString());
+ assertEquals("::ffff:192.168.139.50", fromString("::ffff:192.168.139.50").toString());
+ assertEquals("::ffff:192.168.139.50", fromString("::ffff:c0a8:8b32").toString());
+ }
+
+ @Test
+ public void toLongStringOnSomeRealAddresses()
+ {
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:0001", fromString("::1").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0001:0000", fromString("::1:0").toLongString());
+ assertEquals("0001:0000:0000:0000:0001:0000:0000:0000", fromString("1::1:0:0:0").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:ffff", fromString("::ffff").toLongString());
+ assertEquals("ffff:0000:0000:0000:0000:0000:0000:0000", fromString("ffff::").toLongString());
+ assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toLongString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void constructFromByteArray() throws UnknownHostException
+ {
+ assertEquals("1:1:1:1:1:1:1:1",
+ IPv6Address.fromByteArray(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01})
+ .toString());
+ }
+
+ @Test
+ public void convertToByteArray() throws UnknownHostException
+ {
+ assertArrayEquals(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01},
+ fromString("1:1:1:1:1:1:1:1").toByteArray());
+ }
+
+ @Test
+ public void convertToAndFromByteArray()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ byte[] randomBytes = new byte[16];
+ rg.nextBytes(randomBytes);
+
+ final IPv6Address address = IPv6Address.fromByteArray(randomBytes);
+ assertArrayEquals(randomBytes, address.toByteArray());
+ }
+ }
+
+ @Test
+ public void positionOfLongestRunOfZeroes()
+ {
+ assertArrayEquals(new int[]{0, 8}, fromString("::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{3, 5}, fromString("a:b:c::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{2, 5}, fromString("a:b::c").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{4, 4}, fromString("a:0:0:c::").startAndLengthOfLongestRunOfZeroes());
+ }
+
+ @Test
+ public void toStringCompactsLongestRunOfZeroes()
+ {
+ assertEquals("0:0:1::", fromString("0:0:1::").toString()); // and not ::1:0:0:0:0:0
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("2000::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(15)));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+ @Test
+ public void numberOfLeadingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfLeadingOnes());
+ assertEquals(1, fromString("8000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::f").numberOfLeadingOnes());
+ assertEquals(65, fromString("ffff:ffff:ffff:ffff:8000::f").numberOfLeadingOnes());
+ }
+
+ @Test
+ public void numberOfTrailingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfTrailingZeroes());
+ assertEquals(127, fromString("8000::").numberOfTrailingZeroes());
+ assertEquals(124, fromString("f000::").numberOfTrailingZeroes());
+ assertEquals(0, fromString("f000::f").numberOfTrailingZeroes());
+ assertEquals(63, fromString("ffff:ffff:ffff:ffff:8000::").numberOfTrailingZeroes());
+ }
+
+ @Test
+ public void numberOfLeadingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfLeadingZeroes());
+ assertEquals(0, fromString("8000::").numberOfLeadingZeroes());
+ assertEquals(124, fromString("::f").numberOfLeadingZeroes());
+ assertEquals(63, fromString("::1:ffff:ffff:ffff:ffff").numberOfLeadingZeroes());
+ }
+
+ @Test
+ public void isIPv4Mapped()
+ {
+ assertFalse(fromString("::").isIPv4Mapped());
+ assertFalse(fromString("::0001:ffff:1234:5678").isIPv4Mapped());
+ assertFalse(fromString("1::ffff:1234:5678").isIPv4Mapped());
+ assertFalse(fromString("::afff:1234:5678").isIPv4Mapped());
+
+ assertTrue(fromString("::ffff:1234:5678").isIPv4Mapped());
+ assertTrue(fromString("::ffff:192.168.123.123").isIPv4Mapped());
+ }
+
+ @Test
+ public void isMulticast()
+ {
+ assertFalse(fromString("::").isMulticast());
+
+ assertTrue(fromString("ff12::ffff:1234:5678").isMulticast());
+ }
+
+ @Test
+ public void isLinkLocal()
+ {
+ assertFalse(fromString("::").isLinkLocal());
+
+ assertTrue(fromString("fe80::ffff:1234:5678").isLinkLocal());
+ }
+
+ @Test
+ public void isSiteLocal()
+ {
+ assertFalse(fromString("::").isSiteLocal());
+
+ assertTrue(fromString("fec0::ffff:1234:5678").isSiteLocal());
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..960d630
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
new file mode 100644
index 0000000..0eea592
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkMaskTest
+{
+ @Test
+ public void constructValidNetworkMasks()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)), new IPv6NetworkMask(128));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)), new IPv6NetworkMask(127));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffcL)), new IPv6NetworkMask(126));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)), new IPv6NetworkMask(65));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)), new IPv6NetworkMask(64));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xc000000000000000L, 0x0L)), new IPv6NetworkMask(2));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)), new IPv6NetworkMask(1));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_Negative()
+ {
+ new IPv6NetworkMask(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_TooBig()
+ {
+ new IPv6NetworkMask(129);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromAddress()
+ {
+ IPv6NetworkMask.fromAddress(new IPv6Address(123L, 456L));
+ }
+
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..a40b1bb
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromTwoAddresses()
+ {
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)),
+ IPv6Network.fromTwoAddresses(fromString("::1"), fromString("::2")));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("a:b::"), IPv6NetworkMask.fromPrefixLength(44)),
+ IPv6Network.fromTwoAddresses(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)).toString());
+ assertEquals("a:b:c:d::/64", IPv6Network.fromAddressAndMask(fromString("a:b:c:d::"), IPv6NetworkMask.fromPrefixLength(64))
+ .toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = IPv6Network.fromAddressAndMask(new IPv6Address(rg.nextLong(), rg.nextLong()),
+ IPv6NetworkMask.fromPrefixLength(rg.nextInt(128) + 1));
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+ @Test
+ public void constructAndVerifyPrefixLength()
+ {
+ assertEquals(1, IPv6Network.fromString("a:b:c::/1").getNetmask().asPrefixLength());
+ assertEquals(63, IPv6Network.fromString("a:b:c::/63").getNetmask().asPrefixLength());
+ assertEquals(64, IPv6Network.fromString("a:b:c::/64").getNetmask().asPrefixLength());
+ assertEquals(65, IPv6Network.fromString("a:b:c::/65").getNetmask().asPrefixLength());
+ assertEquals(127, IPv6Network.fromString("a:b:c::/127").getNetmask().asPrefixLength());
+ assertEquals(128, IPv6Network.fromString("a:b:c::/128").getNetmask().asPrefixLength());
+ }
+
+ @Test
+ public void constructAndVerifyNetmask()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)),
+ IPv6Network.fromString("a:b:c::/1").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xfffffffffffffffeL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/63").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/64").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)),
+ IPv6Network.fromString("a:b:c::/65").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)),
+ IPv6Network.fromString("a:b:c::/127").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)),
+ IPv6Network.fromString("a:b:c::/128").getNetmask());
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6Network.fromString("ffff::/8").contains(IPv6Address.fromString("ffff::1")));
+ assertTrue(IPv6Network.fromString("1234:5678:1234:5678::/64").contains(IPv6Address.fromString("1234:5678:1234:5678:1::")));
+ }
+
+ @Test
+ public void zeroNetworkContainsEverything()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+
+ assertTrue(IPv6Network.fromString("::/0").contains(randomAddress));
+ assertTrue(IPv6Network.fromString("abcd:effe:dcba::/0").contains(randomAddress));
+ }
+}
diff --git a/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/examples/Examples.java b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/examples/Examples.java
new file mode 100644
index 0000000..9a1d62b
--- /dev/null
+++ b/tags/java-ipv6-0.12/src/test/java/com/googlecode/ipv6/examples/Examples.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6.examples;
+
+import com.googlecode.ipv6.*;
+import org.junit.Test;
+
+/**
+ * Some examples also featured in the online documentation. This class is in a separate package on purpose, such that we make sure only to
+ * call methods of the public API.
+ *
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address iPv4MappedIPv6Address = IPv6Address.fromString("::ffff:192.168.0.1");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void ipNetworkCalculation()
+ {
+ final IPv6Network strangeNetwork = IPv6Network.fromString("fe80::226:2dff:fefa:cd1f/43");
+
+ System.out.println(strangeNetwork.getFirst()); // prints fe80::
+ System.out.println(strangeNetwork.getLast()); // prints fe80:0:1f:ffff:ffff:ffff:ffff:ffff
+ System.out.println(strangeNetwork.getNetmask().asPrefixLength()); // prints 43
+ System.out.println(strangeNetwork.getNetmask().asAddress()); // prints ffff:ffff:ffe0::
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void ipNetworkMaskConstruction()
+ {
+ final IPv6NetworkMask slash40Network = IPv6NetworkMask.fromPrefixLength(40);
+ System.out.println(slash40Network.asAddress()); // prints ffff:ffff:ff00::
+ System.out.println(slash40Network.asPrefixLength()); // prints 40
+
+ final IPv6NetworkMask slash40NetworkConstructedFromAddressNotation = IPv6NetworkMask.fromAddress(
+ IPv6Address.fromString("ffff:ffff:ff00::"));
+ System.out.println(slash40Network.equals(slash40NetworkConstructedFromAddressNotation)); // prints true
+
+ final IPv6NetworkMask invalidNetworkMask = IPv6NetworkMask.fromAddress(IPv6Address.fromString("0fff::")); // fails
+ }
+
+ @Test
+ public void ipAddressNetworkMasking()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+
+ final IPv6Address masked = iPv6Address.maskWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(masked.toString()); // prints fe80::
+
+ final IPv6Address maximum = iPv6Address.maximumAddressWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(maximum.toString()); // prints fe80:0:ff:ffff:ffff:ffff:ffff:ffff
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(
+ IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff")),
+ IPv6NetworkMask.fromPrefixLength(120));
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/LICENSE b/tags/java-ipv6-0.13/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tags/java-ipv6-0.13/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/java-ipv6-0.13/NOTICE b/tags/java-ipv6-0.13/NOTICE
new file mode 100644
index 0000000..a4605c4
--- /dev/null
+++ b/tags/java-ipv6-0.13/NOTICE
@@ -0,0 +1,2 @@
+ Java IPv6
+ Copyright 2013 Jan Van Besien
\ No newline at end of file
diff --git a/tags/java-ipv6-0.13/pom.xml b/tags/java-ipv6-0.13/pom.xml
new file mode 100644
index 0000000..a1a0b05
--- /dev/null
+++ b/tags/java-ipv6-0.13/pom.xml
@@ -0,0 +1,143 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ 0.13
+ jar
+ Java IPv6 Library
+ http://code.google.com/p/java-ipv6
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.13
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.13
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.13
+
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+
+
+ ${basedir}
+ false
+
+ LICENSE
+ NOTICE
+
+
+
+
+
+
+
+ sonatype-oss-release
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 1.0-beta-4
+
+
+ validate
+
+ create
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/BitSetHelpers.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
new file mode 100644
index 0000000..698c51e
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * This class contains some helpers for working with BitSets. These are generally not necessary in JDK7, since the BitSet.valueOf(long[])
+ * method. However, for java-6 compatibility, we go this way.
+ *
+ * @author Jan Van Besien
+ */
+class BitSetHelpers
+{
+ static BitSet bitSetOf(long lowerBits, long upperBits)
+ {
+ final BitSet bitSet = new BitSet();
+ convert(lowerBits, 0, bitSet);
+ convert(upperBits, Long.SIZE, bitSet);
+ return bitSet;
+ }
+
+ static void convert(long value, int bitSetOffset, BitSet bits)
+ {
+ int index = 0;
+ while (value != 0L)
+ {
+ if (value % 2L != 0)
+ {
+ bits.set(bitSetOffset + index);
+ }
+ ++index;
+ value = value >>> 1;
+ }
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6Address.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6Address.java
new file mode 100644
index 0000000..2954782
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6Address.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.LongBuffer;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private static final int N_BYTES = 16;
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ * IPv6Addresses from a {@link String} or an {@link java.net.InetAddress}. The internal representation of an IPv6Address is exactly
+ * these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
+ *
+ * @param highBits highest order bits
+ * @param lowBits lowest order bits
+ */
+ public static IPv6Address fromLongs(long highBits, long lowBits)
+ {
+ return new IPv6Address(highBits, lowBits);
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String withoutIPv4MappedNotation = IPv6AddressHelpers.rewriteIPv4MappedNotation(string);
+ final String longNotation = IPv6AddressHelpers.expandShortNotation(withoutIPv4MappedNotation);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return IPv6AddressHelpers.mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return IPv6AddressHelpers.parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Create an IPv6 address from a byte array.
+ *
+ * @param bytes byte array with 16 bytes
+ * @return IPv6 address
+ */
+ public static IPv6Address fromByteArray(final byte[] bytes)
+ {
+ if (bytes == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+ if (bytes.length != N_BYTES)
+ throw new IllegalArgumentException("the byte array to construct from should be 16 bytes long");
+
+ ByteBuffer buf = ByteBuffer.allocate(N_BYTES);
+ for (byte b : bytes)
+ {
+ buf.put(b);
+ }
+
+ buf.rewind();
+ LongBuffer longBuffer = buf.asLongBuffer();
+ return new IPv6Address(longBuffer.get(), longBuffer.get());
+ }
+
+ public byte[] toByteArray()
+ {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(N_BYTES).putLong(highBits).putLong(lowBits);
+ return byteBuffer.array();
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something positive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are zero
+ */
+ public IPv6Address maskWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (networkMask.asPrefixLength() == 0)
+ {
+ return new IPv6Address(0, 0);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - networkMask.asPrefixLength())), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are one
+ */
+ public IPv6Address maximumAddressWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> networkMask.asPrefixLength()), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ /**
+ * Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ * and the remaining 32 bits are the IPv4 address.
+ *
+ * @return true if the address is an IPv4-mapped IPv6 addresses.
+ */
+ public boolean isIPv4Mapped()
+ {
+ return this.highBits == 0 // 64 zero bits
+ && (this.lowBits & 0xFFFF000000000000L) == 0 // 16 more zero bits
+ && (this.lowBits & 0x0000FFFF00000000L) == 0x0000FFFF00000000L; // 16 one bits and the remainder is the IPv4 address
+ }
+
+ /**
+ * @return true if the address is an IPv6 multicast address (an address in the network ff00::/8)
+ */
+ public boolean isMulticast()
+ {
+ return IPv6Network.MULTICAST_NETWORK.contains(this);
+ }
+
+ /**
+ * @return true if the address is an IPv6 site-local address (an address in the network fec0::/48)
+ */
+ public boolean isSiteLocal()
+ {
+ return IPv6Network.SITE_LOCAL_NETWORK.contains(this);
+ }
+
+ /**
+ * @return true if the address is an IPv6 link-local address (an address in the network fe80::/48)
+ */
+ public boolean isLinkLocal()
+ {
+ return IPv6Network.LINK_LOCAL_NETWORK.contains(this);
+ }
+
+ /**
+ * Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ * addresses whenever possible.
+ *
+ * @return String representation of the IPv6 address
+ */
+ @Override
+ public String toString()
+ {
+ if (isIPv4Mapped())
+ return toIPv4MappedAddressString();
+ else
+ return toShortHandNotationString();
+ }
+
+ private String toIPv4MappedAddressString()
+ {
+ int byteZero = (int) ((this.lowBits & 0x00000000FF000000L) >> 24);
+ int byteOne = (int) ((this.lowBits & 0x0000000000FF0000L) >> 16);
+ int byteTwo = (int) ((this.lowBits & 0x000000000000FF00L) >> 8);
+ int byteThree = (int) ((this.lowBits & 0x00000000000000FFL));
+
+ final StringBuilder result = new StringBuilder("::ffff:");
+ result.append(byteZero).append(".").append(byteOne).append(".").append(byteTwo).append(".").append(byteThree);
+
+ return result.toString();
+ }
+
+ private String toShortHandNotationString()
+ {
+ final String[] strings = toArrayOfShortStrings();
+
+ final StringBuilder result = new StringBuilder();
+
+ int[] shortHandNotationPositionAndLength = startAndLengthOfLongestRunOfZeroes();
+ int shortHandNotationPosition = shortHandNotationPositionAndLength[0];
+ int shortHandNotationLength = shortHandNotationPositionAndLength[1];
+
+ boolean useShortHandNotation = shortHandNotationLength > 1; // RFC5952 recommends not to use shorthand notation for a single zero
+
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (useShortHandNotation && i == shortHandNotationPosition)
+ {
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(i > shortHandNotationPosition && i < shortHandNotationPosition + shortHandNotationLength))
+ {
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toArrayOfShortStrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%x", shorts[i]);
+ }
+ return strings;
+ }
+
+ /**
+ * @return String representation of the IPv6 address, never using shorthand notation.
+ */
+ public String toLongString()
+ {
+ final String[] strings = toArrayOfZeroPaddedstrings();
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < strings.length - 1; i++)
+ {
+ result.append(strings[i]).append(":");
+ }
+
+ result.append(strings[strings.length - 1]);
+
+ return result.toString();
+ }
+
+ private String[] toArrayOfZeroPaddedstrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%04x", shorts[i]);
+ }
+ return strings;
+ }
+
+ private short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ int[] startAndLengthOfLongestRunOfZeroes()
+ {
+ int longestConsecutiveZeroes = 0;
+ int longestConsecutiveZeroesPos = -1;
+ short[] shorts = toShortArray();
+ for (int pos = 0; pos < shorts.length; pos++)
+ {
+ int consecutiveZeroesAtCurrentPos = countConsecutiveZeroes(shorts, pos);
+ if (consecutiveZeroesAtCurrentPos > longestConsecutiveZeroes)
+ {
+ longestConsecutiveZeroes = consecutiveZeroesAtCurrentPos;
+ longestConsecutiveZeroesPos = pos;
+ }
+ }
+
+ return new int[]{longestConsecutiveZeroesPos, longestConsecutiveZeroes};
+ }
+
+ private int countConsecutiveZeroes(short[] shorts, int offset)
+ {
+ int count = 0;
+ for (int i = offset; i < shorts.length && shorts[i] == 0; i++)
+ {
+ count++;
+ }
+
+ return count;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ public int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ?
+ Long.numberOfTrailingZeros(highBits) + 64 :
+ Long.numberOfTrailingZeros(lowBits);
+ }
+
+ public int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+ public int numberOfLeadingZeroes()
+ {
+ return highBits == 0 ?
+ Long.numberOfLeadingZeros(lowBits) + 64 :
+ Long.numberOfLeadingZeros(highBits);
+ }
+
+ public int numberOfLeadingOnes()
+ {
+ // count leading ones in "value" by counting leading zeroes in "~ value"
+ final IPv6Address flipped = new IPv6Address(~this.highBits, ~this.lowBits);
+ return flipped.numberOfLeadingZeroes();
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..1e4784f
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ private static final Pattern DOT_DELIM = Pattern.compile("\\.");
+
+ /**
+ * Replaces a w.x.y.z substring at the end of the given string with corresponding hexadecimal notation. This is useful in case the
+ * string was using IPv4-Mapped address notation.
+ */
+ static String rewriteIPv4MappedNotation(String string)
+ {
+ if (!string.contains("."))
+ {
+ return string;
+ }
+ else
+ {
+ int lastColon = string.lastIndexOf(":");
+ String firstPart = string.substring(0, lastColon + 1);
+ String mappedIPv4Part = string.substring(lastColon + 1);
+
+ if (mappedIPv4Part.contains("."))
+ {
+ String[] dotSplits = DOT_DELIM.split(mappedIPv4Part);
+ if (dotSplits.length != 4)
+ throw new IllegalArgumentException(String.format("can not parse [%s]", string));
+
+ StringBuilder rewrittenString = new StringBuilder();
+ rewrittenString.append(firstPart);
+ int byteZero = Integer.parseInt(dotSplits[0]);
+ int byteOne = Integer.parseInt(dotSplits[1]);
+ int byteTwo = Integer.parseInt(dotSplits[2]);
+ int byteThree = Integer.parseInt(dotSplits[3]);
+
+ rewrittenString.append(String.format("%02x", byteZero));
+ rewrittenString.append(String.format("%02x", byteOne));
+ rewrittenString.append(":");
+ rewrittenString.append(String.format("%02x", byteTwo));
+ rewrittenString.append(String.format("%02x", byteThree));
+
+ return rewrittenString.toString();
+ }
+ else
+ {
+ throw new IllegalArgumentException(String.format("can not parse [%s]", string));
+ }
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..77b9a0f
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+
+import java.util.*;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final IPv6NetworkMask allocationSubnetSize;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ * size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ * (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ */
+ public static IPv6AddressPool fromRangeAndSubnet(final IPv6AddressRange range,
+ final IPv6NetworkMask allocationSubnetSize)
+ {
+ // in the beginning, all is free
+ return new IPv6AddressPool(range, allocationSubnetSize, new TreeSet(Arrays.asList(range)), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6AddressRange range, final IPv6NetworkMask allocationSubnetSize,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = range;
+
+ this.allocationSubnetSize = allocationSubnetSize;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(underlyingRange, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenSize(underlyingRange, allocationSubnetSize);
+ }
+
+ private void validateFreeRanges(IPv6AddressRange range, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(range, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be useful. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6AddressRange range, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(range.getFirst()) >= 0
+ && toValidate.last().getLast().compareTo(range.getLast()) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenSize(IPv6AddressRange range, IPv6NetworkMask allocationSubnetSize)
+ {
+ final int allocatableBits = 128 - allocationSubnetSize.asPrefixLength();
+
+ if (range.getFirst().numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength() + "], "
+ + "first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (range.getLast().numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength()
+ + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = IPv6Network.fromAddressAndMask(firstFreeRange.getFirst(), allocationSubnetSize);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (!this.allocationSubnetSize.equals(toAllocate.getNetmask()))
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getNetmask().asPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /"
+ + allocationSubnetSize);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (!this.allocationSubnetSize.equals(network.getNetmask()))
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getNetmask().asPrefixLength()
+ + "] can not be free in a pool which uses prefix length [" +
+ allocationSubnetSize + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (allocationSubnetSize != null ? !allocationSubnetSize.equals(that.allocationSubnetSize) : that.allocationSubnetSize != null)
+ return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + (allocationSubnetSize != null ? allocationSubnetSize.hashCode() : 0);
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return underlyingRange.toLongString();
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..6d2a925
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.*;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public static IPv6AddressRange fromFirstAndLast(IPv6Address first, IPv6Address last)
+ {
+ return new IPv6AddressRange(first, last);
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(fromFirstAndLast(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(fromFirstAndLast(first, last.subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, address.subtract(1)),
+ fromFirstAndLast(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return fromFirstAndLast(address, last);
+ else if (address.compareTo(last) > 0)
+ return fromFirstAndLast(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(fromFirstAndLast(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(fromFirstAndLast(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, network.getFirst().subtract(1)),
+ fromFirstAndLast(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return first.toLongString() + " - " + last.toLongString();
+ }
+
+ /**
+ * The natural order of {@link com.googlecode.ipv6.IPv6AddressRange}s orders them on increasing first addresses, and on increasing last
+ * address if the first address would be equal.
+ *
+ * Note that the natural order does thus not compare sizes of ranges.
+ *
+ * @param that range to compare with
+ * @return negative, zero or positive depending on whether this is smaller, equal or greater than that
+ */
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (!this.first.equals(that.first))
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ {
+ IPv6Address result = current;
+ current = current.add(1);
+ return result;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ IPv6AddressRange.this.remove(current);
+ }
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6Network.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6Network.java
new file mode 100644
index 0000000..49f23b0
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6Network.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ public static final IPv6Network MULTICAST_NETWORK = fromString("ff00::/8");
+
+ public static final IPv6Network SITE_LOCAL_NETWORK = fromString("fec0::/48");
+
+ public static final IPv6Network LINK_LOCAL_NETWORK = fromString("fe80::/64");
+
+
+ private final IPv6Address address;
+
+ private final IPv6NetworkMask networkMask;
+
+ /**
+ * Construct from address and network mask.
+ *
+ * @param address address
+ * @param networkMask network mask
+ */
+ private IPv6Network(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ super(address.maskWithNetworkMask(networkMask), address.maximumAddressWithNetworkMask(networkMask));
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Create an IPv6 network from an IPv6Address and an IPv6NetworkMask
+ *
+ * @param address IPv6 address (the network address or any other address within the network)
+ * @param networkMask IPv6 network mask
+ * @return IPv6 network
+ */
+ public static IPv6Network fromAddressAndMask(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ return new IPv6Network(address, networkMask);
+ }
+
+ /**
+ * Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ * length") which contains both addresses.
+ *
+ * @param one address one
+ * @param two address two, should be bigger than address one
+ */
+ public static IPv6Network fromTwoAddresses(IPv6Address one, IPv6Address two)
+ {
+ final IPv6NetworkMask longestPrefixLength = IPv6NetworkMask.fromPrefixLength(IPv6NetworkHelpers.longestPrefixLength(one, two));
+ return new IPv6Network(one.maskWithNetworkMask(longestPrefixLength), longestPrefixLength);
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 network
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return fromAddressAndMask(networkAddress, new IPv6NetworkMask(prefixLength));
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + networkMask.asPrefixLength();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return address.toLongString() + "/" + networkMask.asPrefixLength();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+ if (networkMask != null ? !networkMask.equals(that.networkMask) : that.networkMask != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = super.hashCode();
+ result = 31 * result + (address != null ? address.hashCode() : 0);
+ result = 31 * result + (networkMask != null ? networkMask.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6NetworkMask getNetmask()
+ {
+ return networkMask;
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..66268ed
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = bitSetOf(first.getLowBits(), first.getHighBits());
+ final BitSet lastBits = bitSetOf(last.getLowBits(), last.getHighBits());
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet a, BitSet b)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (a.get(i) == b.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
new file mode 100644
index 0000000..ee4394b
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ * starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6NetworkMask
+{
+ private final int prefixLength;
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ IPv6NetworkMask(int prefixLength)
+ {
+ if (prefixLength < 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval [0, 128]");
+
+ this.prefixLength = prefixLength;
+ }
+
+
+ /**
+ * Construct an IPv6 network mask from an IPv6 address. The address should be a valid network mask.
+ *
+ * @param iPv6Address address to use as network mask
+ * @throws IllegalArgumentException if the address is not a valid network mask
+ */
+ public static IPv6NetworkMask fromAddress(final IPv6Address iPv6Address)
+ {
+ validateNetworkMask(iPv6Address);
+ return new IPv6NetworkMask(iPv6Address.numberOfLeadingOnes());
+ }
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ public static IPv6NetworkMask fromPrefixLength(int prefixLength)
+ {
+ return new IPv6NetworkMask(prefixLength);
+ }
+
+ private static void validateNetworkMask(IPv6Address addressToValidate)
+ {
+ final BitSet addressAsBitSet = bitSetOf(addressToValidate.getLowBits(), addressToValidate.getHighBits());
+ if (!addressAsBitSet.get(127))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ else
+ {
+ boolean firstZeroFound = false;
+ for (int i = 127; i >= 0 && !firstZeroFound; i--)
+ {
+ if (!addressAsBitSet.get(i))
+ {
+ firstZeroFound = true;
+
+ // a zero -> all the others should also be zero
+ for (int j = i - 1; j >= 0; j--)
+ {
+ if (addressAsBitSet.get(j))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int asPrefixLength()
+ {
+ return prefixLength;
+ }
+
+ public IPv6Address asAddress()
+ {
+ if (prefixLength == 128)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0L);
+ }
+ else if (prefixLength > 64)
+ {
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL << (64 - prefixLength), 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6NetworkMask that = (IPv6NetworkMask) o;
+
+ if (prefixLength != that.prefixLength) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefixLength;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "" + prefixLength;
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..834d561
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::1"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:fffe")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::ab00"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("2001::ffff:ffff")), new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::abcd:ef00"),
+ fromString("2001::abcd:efff")), new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null",
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::99"), IPv6NetworkMask.fromPrefixLength(128)));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(66));
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..e699741
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ IPv6AddressRange.fromFirstAndLast(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(IPv6AddressRange.fromFirstAndLast(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4"))
+ .size());
+ assertEquals(1, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8"))
+ .size());
+ assertEquals(0, IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4"))
+ .size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : IPv6AddressRange.fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+ @Test
+ public void compareTo()
+ {
+ final IPv6AddressRange a =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange b =
+ IPv6AddressRange.fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange c =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange d =
+ IPv6AddressRange.fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+
+ Assert.assertTrue(a.compareTo(b) > 0);
+ Assert.assertTrue(a.compareTo(c) < 0);
+ Assert.assertTrue(a.compareTo(d) < 0);
+ Assert.assertTrue(b.compareTo(c) < 0);
+ Assert.assertTrue(b.compareTo(d) < 0);
+ Assert.assertTrue(c.compareTo(d) > 0);
+
+ Assert.assertTrue(a.compareTo(a) == 0);
+ Assert.assertTrue(b.compareTo(b) == 0);
+ Assert.assertTrue(c.compareTo(c) == 0);
+ Assert.assertTrue(d.compareTo(d) == 0);
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..3536ec3
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromInetAddress;
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesFromRFC5952()
+ {
+ assertEquals("::", fromString("::").toString());
+ assertEquals("1:2:3:4::", fromString("1:2:3:4::").toString());
+ assertEquals("::1:2:3:4", fromString("::1:2:3:4").toString());
+ assertEquals("1::2", fromString("1::2").toString());
+ assertEquals("::2", fromString("::2").toString());
+ assertEquals("1::", fromString("1::").toString());
+ assertEquals("a31:200:3abc::de4", fromString("0a31:0200:3AbC::0dE4").toString());
+ assertEquals("1::4:0:0:0", fromString("1:0:0:0:4:0:0:0").toString());
+
+ assertEquals("2001:db8::1", fromString("2001:db8::1").toString());
+ assertEquals("2001:db8::2:1", fromString("2001:db8:0:0:0:0:2:1").toString());
+ assertEquals("2001:db8:0:1:1:1:1:1", fromString("2001:db8:0:1:1:1:1:1").toString());
+ assertEquals("2001:db8::1:0:0:1", fromString("2001:db8::1:0:0:1").toString());
+ assertEquals("2001:0:0:1::1", fromString("2001:0:0:1:0:0:0:1").toString());
+
+ assertEquals("1:0:0:4::", fromString("1:0:0:4::").toString());
+ }
+
+ @Test
+ public void parseSomeRealIPv4MappedAddresses()
+ {
+ assertEquals("::ffff:0.0.0.1", fromString("::ffff:0.0.0.1").toString());
+ assertEquals("::ffff:192.168.139.50", fromString("::ffff:192.168.139.50").toString());
+ assertEquals("::ffff:192.168.139.50", fromString("::ffff:c0a8:8b32").toString());
+ }
+
+ @Test
+ public void toLongStringOnSomeRealAddresses()
+ {
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:0001", fromString("::1").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0001:0000", fromString("::1:0").toLongString());
+ assertEquals("0001:0000:0000:0000:0001:0000:0000:0000", fromString("1::1:0:0:0").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:ffff", fromString("::ffff").toLongString());
+ assertEquals("ffff:0000:0000:0000:0000:0000:0000:0000", fromString("ffff::").toLongString());
+ assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toLongString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void constructFromByteArray() throws UnknownHostException
+ {
+ assertEquals("1:1:1:1:1:1:1:1",
+ IPv6Address.fromByteArray(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01})
+ .toString());
+ }
+
+ @Test
+ public void convertToByteArray() throws UnknownHostException
+ {
+ assertArrayEquals(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01},
+ fromString("1:1:1:1:1:1:1:1").toByteArray());
+ }
+
+ @Test
+ public void convertToAndFromByteArray()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ byte[] randomBytes = new byte[16];
+ rg.nextBytes(randomBytes);
+
+ final IPv6Address address = IPv6Address.fromByteArray(randomBytes);
+ assertArrayEquals(randomBytes, address.toByteArray());
+ }
+ }
+
+ @Test
+ public void positionOfLongestRunOfZeroes()
+ {
+ assertArrayEquals(new int[]{0, 8}, fromString("::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{3, 5}, fromString("a:b:c::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{2, 5}, fromString("a:b::c").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{4, 4}, fromString("a:0:0:c::").startAndLengthOfLongestRunOfZeroes());
+ }
+
+ @Test
+ public void toStringCompactsLongestRunOfZeroes()
+ {
+ assertEquals("0:0:1::", fromString("0:0:1::").toString()); // and not ::1:0:0:0:0:0
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("2000::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(15)));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+ @Test
+ public void numberOfLeadingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfLeadingOnes());
+ assertEquals(1, fromString("8000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::f").numberOfLeadingOnes());
+ assertEquals(65, fromString("ffff:ffff:ffff:ffff:8000::f").numberOfLeadingOnes());
+ }
+
+ @Test
+ public void numberOfTrailingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfTrailingZeroes());
+ assertEquals(127, fromString("8000::").numberOfTrailingZeroes());
+ assertEquals(124, fromString("f000::").numberOfTrailingZeroes());
+ assertEquals(0, fromString("f000::f").numberOfTrailingZeroes());
+ assertEquals(63, fromString("ffff:ffff:ffff:ffff:8000::").numberOfTrailingZeroes());
+ }
+
+ @Test
+ public void numberOfLeadingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfLeadingZeroes());
+ assertEquals(0, fromString("8000::").numberOfLeadingZeroes());
+ assertEquals(124, fromString("::f").numberOfLeadingZeroes());
+ assertEquals(63, fromString("::1:ffff:ffff:ffff:ffff").numberOfLeadingZeroes());
+ }
+
+ @Test
+ public void isIPv4Mapped()
+ {
+ assertFalse(fromString("::").isIPv4Mapped());
+ assertFalse(fromString("::0001:ffff:1234:5678").isIPv4Mapped());
+ assertFalse(fromString("1::ffff:1234:5678").isIPv4Mapped());
+ assertFalse(fromString("::afff:1234:5678").isIPv4Mapped());
+
+ assertTrue(fromString("::ffff:1234:5678").isIPv4Mapped());
+ assertTrue(fromString("::ffff:192.168.123.123").isIPv4Mapped());
+ }
+
+ @Test
+ public void isMulticast()
+ {
+ assertFalse(fromString("::").isMulticast());
+
+ assertTrue(fromString("ff12::ffff:1234:5678").isMulticast());
+ }
+
+ @Test
+ public void isLinkLocal()
+ {
+ assertFalse(fromString("::").isLinkLocal());
+
+ assertTrue(fromString("fe80::ffff:1234:5678").isLinkLocal());
+ }
+
+ @Test
+ public void isSiteLocal()
+ {
+ assertFalse(fromString("::").isSiteLocal());
+
+ assertTrue(fromString("fec0::ffff:1234:5678").isSiteLocal());
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..960d630
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
new file mode 100644
index 0000000..0eea592
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkMaskTest
+{
+ @Test
+ public void constructValidNetworkMasks()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)), new IPv6NetworkMask(128));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)), new IPv6NetworkMask(127));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffcL)), new IPv6NetworkMask(126));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)), new IPv6NetworkMask(65));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)), new IPv6NetworkMask(64));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xc000000000000000L, 0x0L)), new IPv6NetworkMask(2));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)), new IPv6NetworkMask(1));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_Negative()
+ {
+ new IPv6NetworkMask(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_TooBig()
+ {
+ new IPv6NetworkMask(129);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromAddress()
+ {
+ IPv6NetworkMask.fromAddress(new IPv6Address(123L, 456L));
+ }
+
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..da26678
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromTwoAddresses()
+ {
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)),
+ IPv6Network.fromTwoAddresses(fromString("::1"), fromString("::2")));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("a:b::"), IPv6NetworkMask.fromPrefixLength(44)),
+ IPv6Network.fromTwoAddresses(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)).toString());
+ assertEquals("a:b:c:d::/64", IPv6Network.fromAddressAndMask(fromString("a:b:c:d::"), IPv6NetworkMask.fromPrefixLength(64))
+ .toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = IPv6Network.fromAddressAndMask(new IPv6Address(rg.nextLong(), rg.nextLong()),
+ IPv6NetworkMask.fromPrefixLength(rg.nextInt(128) + 1));
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+ @Test
+ public void constructAndVerifyPrefixLength()
+ {
+ assertEquals(1, IPv6Network.fromString("a:b:c::/1").getNetmask().asPrefixLength());
+ assertEquals(63, IPv6Network.fromString("a:b:c::/63").getNetmask().asPrefixLength());
+ assertEquals(64, IPv6Network.fromString("a:b:c::/64").getNetmask().asPrefixLength());
+ assertEquals(65, IPv6Network.fromString("a:b:c::/65").getNetmask().asPrefixLength());
+ assertEquals(127, IPv6Network.fromString("a:b:c::/127").getNetmask().asPrefixLength());
+ assertEquals(128, IPv6Network.fromString("a:b:c::/128").getNetmask().asPrefixLength());
+ }
+
+ @Test
+ public void constructAndVerifyNetmask()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)),
+ IPv6Network.fromString("a:b:c::/1").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xfffffffffffffffeL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/63").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/64").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)),
+ IPv6Network.fromString("a:b:c::/65").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)),
+ IPv6Network.fromString("a:b:c::/127").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)),
+ IPv6Network.fromString("a:b:c::/128").getNetmask());
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6Network.fromString("ffff::/8").contains(IPv6Address.fromString("ffff::1")));
+ assertTrue(IPv6Network.fromString("1234:5678:1234:5678::/64").contains(IPv6Address.fromString("1234:5678:1234:5678:1::")));
+ }
+
+ @Test
+ public void zeroNetworkContainsEverything()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+
+ assertTrue(IPv6Network.fromString("::/0").contains(randomAddress));
+ assertTrue(IPv6Network.fromString("abcd:effe:dcba::/0").contains(randomAddress));
+ }
+
+ @Test
+ public void iteratorShouldStartWithFirstAndEndWithLast()
+ {
+ IPv6Network ipv6Network = IPv6Network.fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000/126");
+ Iterator iterator = ipv6Network.iterator();
+ int i = 0;
+ for (; iterator.hasNext(); i++)
+ {
+ assertEquals(IPv6Address.fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:000" + i), iterator.next());
+ }
+ assertEquals(4, i);
+ }
+}
diff --git a/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/examples/Examples.java b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/examples/Examples.java
new file mode 100644
index 0000000..9a1d62b
--- /dev/null
+++ b/tags/java-ipv6-0.13/src/test/java/com/googlecode/ipv6/examples/Examples.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6.examples;
+
+import com.googlecode.ipv6.*;
+import org.junit.Test;
+
+/**
+ * Some examples also featured in the online documentation. This class is in a separate package on purpose, such that we make sure only to
+ * call methods of the public API.
+ *
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address iPv4MappedIPv6Address = IPv6Address.fromString("::ffff:192.168.0.1");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void ipNetworkCalculation()
+ {
+ final IPv6Network strangeNetwork = IPv6Network.fromString("fe80::226:2dff:fefa:cd1f/43");
+
+ System.out.println(strangeNetwork.getFirst()); // prints fe80::
+ System.out.println(strangeNetwork.getLast()); // prints fe80:0:1f:ffff:ffff:ffff:ffff:ffff
+ System.out.println(strangeNetwork.getNetmask().asPrefixLength()); // prints 43
+ System.out.println(strangeNetwork.getNetmask().asAddress()); // prints ffff:ffff:ffe0::
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void ipNetworkMaskConstruction()
+ {
+ final IPv6NetworkMask slash40Network = IPv6NetworkMask.fromPrefixLength(40);
+ System.out.println(slash40Network.asAddress()); // prints ffff:ffff:ff00::
+ System.out.println(slash40Network.asPrefixLength()); // prints 40
+
+ final IPv6NetworkMask slash40NetworkConstructedFromAddressNotation = IPv6NetworkMask.fromAddress(
+ IPv6Address.fromString("ffff:ffff:ff00::"));
+ System.out.println(slash40Network.equals(slash40NetworkConstructedFromAddressNotation)); // prints true
+
+ final IPv6NetworkMask invalidNetworkMask = IPv6NetworkMask.fromAddress(IPv6Address.fromString("0fff::")); // fails
+ }
+
+ @Test
+ public void ipAddressNetworkMasking()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+
+ final IPv6Address masked = iPv6Address.maskWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(masked.toString()); // prints fe80::
+
+ final IPv6Address maximum = iPv6Address.maximumAddressWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(maximum.toString()); // prints fe80:0:ff:ffff:ffff:ffff:ffff:ffff
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(
+ IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff")),
+ IPv6NetworkMask.fromPrefixLength(120));
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/LICENSE b/tags/java-ipv6-0.14/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tags/java-ipv6-0.14/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/java-ipv6-0.14/NOTICE b/tags/java-ipv6-0.14/NOTICE
new file mode 100644
index 0000000..a4605c4
--- /dev/null
+++ b/tags/java-ipv6-0.14/NOTICE
@@ -0,0 +1,2 @@
+ Java IPv6
+ Copyright 2013 Jan Van Besien
\ No newline at end of file
diff --git a/tags/java-ipv6-0.14/pom.xml b/tags/java-ipv6-0.14/pom.xml
new file mode 100644
index 0000000..e278985
--- /dev/null
+++ b/tags/java-ipv6-0.14/pom.xml
@@ -0,0 +1,143 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.googlecode.java-ipv6
+ java-ipv6
+ 0.14
+ jar
+ Java IPv6 Library
+ http://code.google.com/p/java-ipv6
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.14
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.14
+ scm:svn:https://java-ipv6.googlecode.com/svn/tags/java-ipv6-0.14
+
+
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.1
+
+ https://java-ipv6.googlecode.com/svn/tags/
+
+
+
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
+
+
+ ${basedir}
+ false
+
+ LICENSE
+ NOTICE
+
+
+
+
+
+
+
+ sonatype-oss-release
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 1.0-beta-4
+
+
+ validate
+
+ create
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/BitSetHelpers.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
new file mode 100644
index 0000000..698c51e
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/BitSetHelpers.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+/**
+ * This class contains some helpers for working with BitSets. These are generally not necessary in JDK7, since the BitSet.valueOf(long[])
+ * method. However, for java-6 compatibility, we go this way.
+ *
+ * @author Jan Van Besien
+ */
+class BitSetHelpers
+{
+ static BitSet bitSetOf(long lowerBits, long upperBits)
+ {
+ final BitSet bitSet = new BitSet();
+ convert(lowerBits, 0, bitSet);
+ convert(upperBits, Long.SIZE, bitSet);
+ return bitSet;
+ }
+
+ static void convert(long value, int bitSetOffset, BitSet bits)
+ {
+ int index = 0;
+ while (value != 0L)
+ {
+ if (value % 2L != 0)
+ {
+ bits.set(bitSetOffset + index);
+ }
+ ++index;
+ value = value >>> 1;
+ }
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6Address.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6Address.java
new file mode 100644
index 0000000..73b6e33
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6Address.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.math.BigInteger;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.LongBuffer;
+import java.util.Arrays;
+
+import static com.googlecode.ipv6.IPv6AddressHelpers.prefixWithZeroBytes;
+
+/**
+ * Immutable representation of an IPv6 address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Address implements Comparable
+{
+ private static final int N_SHORTS = 8;
+
+ private static final int N_BYTES = 16;
+
+ public static final IPv6Address MAX = IPv6Address.fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+
+ private final long highBits;
+
+ private final long lowBits;
+
+ IPv6Address(long highBits, long lowBits)
+ {
+ this.highBits = highBits;
+ this.lowBits = lowBits;
+ }
+
+ /**
+ * Construct an IPv6Address from two longs representing the 64 highest and 64 lowest bits. It is usually easier to construct
+ * IPv6Addresses from a {@link String} or an {@link java.net.InetAddress}. The internal representation of an IPv6Address is exactly
+ * these two longs though, so if you already happen to have them, this provides a very efficient way to construct an IPv6Address.
+ *
+ * @param highBits highest order bits
+ * @param lowBits lowest order bits
+ */
+ public static IPv6Address fromLongs(long highBits, long lowBits)
+ {
+ return new IPv6Address(highBits, lowBits);
+ }
+
+ /**
+ * Create an IPv6 address from its String representation. For example "1234:5678:abcd:0000:9876:3210:ffff:ffff" or "2001::ff" or even
+ * "::". IPv4-Mapped IPv6 addresses such as "::ffff:123.456.123.456" are also supported.
+ *
+ * @param string string representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromString(final String string)
+ {
+ if (string == null)
+ throw new IllegalArgumentException("can not parse [null]");
+
+ final String withoutIPv4MappedNotation = IPv6AddressHelpers.rewriteIPv4MappedNotation(string);
+ final String longNotation = IPv6AddressHelpers.expandShortNotation(withoutIPv4MappedNotation);
+
+ final long[] longs = tryParseStringArrayIntoLongArray(string, longNotation);
+
+ IPv6AddressHelpers.validateLongs(longs);
+
+ return IPv6AddressHelpers.mergeLongArrayIntoIPv6Address(longs);
+ }
+
+ private static long[] tryParseStringArrayIntoLongArray(String string, String longNotation)
+ {
+ try
+ {
+ return IPv6AddressHelpers.parseStringArrayIntoLongArray(longNotation.split(":"));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("can not parse [" + string + "]");
+ }
+ }
+
+ /**
+ * Create an IPv6 address from a java.net.Inet6Address.
+ *
+ * @param inetAddress Inet6Address representation
+ * @return IPv6 address
+ */
+ public static IPv6Address fromInetAddress(final InetAddress inetAddress)
+ {
+ if (inetAddress == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+
+ return fromString(inetAddress.getHostAddress());
+ }
+
+ public InetAddress toInetAddress() throws UnknownHostException
+ {
+ return Inet6Address.getByName(toString());
+ }
+
+ /**
+ * Create an IPv6 address from a byte array.
+ *
+ * @param bytes byte array with 16 bytes (interpreted unsigned)
+ * @return IPv6 address
+ */
+ public static IPv6Address fromByteArray(final byte[] bytes)
+ {
+ if (bytes == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+ if (bytes.length != N_BYTES)
+ throw new IllegalArgumentException("the byte array to construct from should be 16 bytes long");
+
+ ByteBuffer buf = ByteBuffer.allocate(N_BYTES);
+ for (byte b : bytes)
+ {
+ buf.put(b);
+ }
+
+ buf.rewind();
+ LongBuffer longBuffer = buf.asLongBuffer();
+ return new IPv6Address(longBuffer.get(), longBuffer.get());
+ }
+
+ /**
+ * @return byte[] representation
+ */
+ public byte[] toByteArray()
+ {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(N_BYTES).putLong(highBits).putLong(lowBits);
+ return byteBuffer.array();
+ }
+
+ /**
+ * Create an IPv6 address from a (positive) {@link java.math.BigInteger}. The magnitude of the {@link java.math.BigInteger} represents
+ * the IPv6 address value. Or in other words, the {@link java.math.BigInteger} with value N defines the Nth possible IPv6 address.
+ *
+ * @param bigInteger {@link java.math.BigInteger} value
+ * @return IPv6 address
+ */
+ public static IPv6Address fromBigInteger(final BigInteger bigInteger)
+ {
+ if (bigInteger == null)
+ throw new IllegalArgumentException("can not construct from [null]");
+ if (bigInteger.compareTo(BigInteger.ZERO) < 0)
+ throw new IllegalArgumentException("can not construct from negative value");
+ if (bigInteger.compareTo(MAX.toBigInteger()) > 0)
+ throw new IllegalArgumentException("bigInteger represents a value bigger than 2^128 - 1");
+
+ byte[] bytes = bigInteger.toByteArray();
+
+ if (bytes[0] == 0)
+ {
+ // a zero byte was added to represent the (always positive, hence zero) sign bit
+ return fromByteArray(prefixWithZeroBytes(Arrays.copyOfRange(bytes, 1, bytes.length), N_BYTES));
+ }
+ else
+ {
+ return fromByteArray(prefixWithZeroBytes(bytes, N_BYTES));
+ }
+ }
+
+ /**
+ * @return {@link java.math.BigInteger} representation. The magnitude of the {@link java.math.BigInteger} represents the IPv6 address
+ * value. Or in other words, the {@link java.math.BigInteger} with value N defines the Nth possible IPv6 address.
+ */
+ public BigInteger toBigInteger()
+ {
+ return new BigInteger(1, toByteArray());
+ }
+
+ /**
+ * Addition. Will never overflow, but wraps around when the highest ip address has been reached.
+ *
+ * @param value value to add
+ * @return new IPv6 address
+ */
+ public IPv6Address add(int value)
+ {
+ final long newLowBits = lowBits + value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we added something positive and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we added something negative and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Subtraction. Will never underflow, but wraps around when the lowest ip address has been reached.
+ *
+ * @param value value to substract
+ * @return new IPv6 address
+ */
+ public IPv6Address subtract(int value)
+ {
+ final long newLowBits = lowBits - value;
+
+ if (value >= 0)
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(lowBits, newLowBits))
+ {
+ // oops, we subtracted something postive and the result is bigger -> overflow detected (carry over one bit from high to low)
+ return new IPv6Address(highBits - 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ else
+ {
+ if (IPv6AddressHelpers.isLessThanUnsigned(newLowBits, lowBits))
+ {
+ // oops, we subtracted something negative and the result is smaller -> overflow detected (carry over one bit from low to high)
+ return new IPv6Address(highBits + 1, newLowBits);
+ }
+ else
+ {
+ // no overflow
+ return new IPv6Address(highBits, newLowBits);
+ }
+ }
+ }
+
+ /**
+ * Mask the address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are zero
+ */
+ public IPv6Address maskWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0);
+ }
+ else if (networkMask.asPrefixLength() == 0)
+ {
+ return new IPv6Address(0, 0);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits & (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 0
+ return new IPv6Address(this.highBits & (0xFFFFFFFFFFFFFFFFL << (64 - networkMask.asPrefixLength())), 0);
+ }
+ }
+
+ /**
+ * Calculate the maximum address with the given network mask.
+ *
+ * @param networkMask network mask
+ * @return an address of which the last 128 - networkMask.asPrefixLength() bits are one
+ */
+ public IPv6Address maximumAddressWithNetworkMask(final IPv6NetworkMask networkMask)
+ {
+ if (networkMask.asPrefixLength() == 128)
+ {
+ return this;
+ }
+ else if (networkMask.asPrefixLength() == 64)
+ {
+ return new IPv6Address(this.highBits, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (networkMask.asPrefixLength() > 64)
+ {
+ // apply mask on low bits only
+ final int remainingPrefixLength = networkMask.asPrefixLength() - 64;
+ return new IPv6Address(this.highBits, this.lowBits | (0xFFFFFFFFFFFFFFFFL >>> remainingPrefixLength));
+ }
+ else
+ {
+ // apply mask on high bits, low bits completely 1
+ return new IPv6Address(this.highBits | (0xFFFFFFFFFFFFFFFFL >>> networkMask.asPrefixLength()), 0xFFFFFFFFFFFFFFFFL);
+ }
+ }
+
+ /**
+ * Set a bit in the address.
+ *
+ * @param bit to set (in the range [0, 127])
+ * @return an address with the given bit set
+ */
+ public IPv6Address setBit(final int bit)
+ {
+ if (bit < 0 || bit > 127)
+ throw new IllegalArgumentException("can only set bits in the interval [0, 127]");
+
+ if (bit < 64)
+ {
+ return new IPv6Address(this.highBits, this.lowBits | (1 << bit));
+ }
+ else
+ {
+ return new IPv6Address(this.highBits | (1 << (bit - 64)), this.lowBits);
+ }
+ }
+
+ /**
+ * Returns true if the address is an IPv4-mapped IPv6 address. In these addresses, the first 80 bits are zero, the next 16 bits are one,
+ * and the remaining 32 bits are the IPv4 address.
+ *
+ * @return true if the address is an IPv4-mapped IPv6 addresses.
+ */
+ public boolean isIPv4Mapped()
+ {
+ return this.highBits == 0 // 64 zero bits
+ && (this.lowBits & 0xFFFF000000000000L) == 0 // 16 more zero bits
+ && (this.lowBits & 0x0000FFFF00000000L) == 0x0000FFFF00000000L; // 16 one bits and the remainder is the IPv4 address
+ }
+
+ /**
+ * @return true if the address is an IPv6 multicast address (an address in the network ff00::/8)
+ */
+ public boolean isMulticast()
+ {
+ return IPv6Network.MULTICAST_NETWORK.contains(this);
+ }
+
+ /**
+ * @return true if the address is an IPv6 site-local address (an address in the network fec0::/48)
+ */
+ public boolean isSiteLocal()
+ {
+ return IPv6Network.SITE_LOCAL_NETWORK.contains(this);
+ }
+
+ /**
+ * @return true if the address is an IPv6 link-local address (an address in the network fe80::/48)
+ */
+ public boolean isLinkLocal()
+ {
+ return IPv6Network.LINK_LOCAL_NETWORK.contains(this);
+ }
+
+ /**
+ * Returns a string representation of the IPv6 address. It will use shorthand notation and special notation for IPv4-mapped IPv6
+ * addresses whenever possible.
+ *
+ * @return String representation of the IPv6 address
+ */
+ @Override
+ public String toString()
+ {
+ if (isIPv4Mapped())
+ return toIPv4MappedAddressString();
+ else
+ return toShortHandNotationString();
+ }
+
+ private String toIPv4MappedAddressString()
+ {
+ int byteZero = (int) ((this.lowBits & 0x00000000FF000000L) >> 24);
+ int byteOne = (int) ((this.lowBits & 0x0000000000FF0000L) >> 16);
+ int byteTwo = (int) ((this.lowBits & 0x000000000000FF00L) >> 8);
+ int byteThree = (int) ((this.lowBits & 0x00000000000000FFL));
+
+ final StringBuilder result = new StringBuilder("::ffff:");
+ result.append(byteZero).append(".").append(byteOne).append(".").append(byteTwo).append(".").append(byteThree);
+
+ return result.toString();
+ }
+
+ private String toShortHandNotationString()
+ {
+ final String[] strings = toArrayOfShortStrings();
+
+ final StringBuilder result = new StringBuilder();
+
+ int[] shortHandNotationPositionAndLength = startAndLengthOfLongestRunOfZeroes();
+ int shortHandNotationPosition = shortHandNotationPositionAndLength[0];
+ int shortHandNotationLength = shortHandNotationPositionAndLength[1];
+
+ boolean useShortHandNotation = shortHandNotationLength > 1; // RFC5952 recommends not to use shorthand notation for a single zero
+
+ for (int i = 0; i < strings.length; i++)
+ {
+ if (useShortHandNotation && i == shortHandNotationPosition)
+ {
+ if (i == 0)
+ result.append("::");
+ else
+ result.append(":");
+ }
+ else if (!(i > shortHandNotationPosition && i < shortHandNotationPosition + shortHandNotationLength))
+ {
+ result.append(strings[i]);
+ if (i < N_SHORTS - 1)
+ result.append(":");
+ }
+ }
+
+ return result.toString().toLowerCase();
+ }
+
+ private String[] toArrayOfShortStrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%x", shorts[i]);
+ }
+ return strings;
+ }
+
+ /**
+ * @return String representation of the IPv6 address, never using shorthand notation.
+ */
+ public String toLongString()
+ {
+ final String[] strings = toArrayOfZeroPaddedstrings();
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < strings.length - 1; i++)
+ {
+ result.append(strings[i]).append(":");
+ }
+
+ result.append(strings[strings.length - 1]);
+
+ return result.toString();
+ }
+
+ private String[] toArrayOfZeroPaddedstrings()
+ {
+ final short[] shorts = toShortArray();
+ final String[] strings = new String[shorts.length];
+ for (int i = 0; i < shorts.length; i++)
+ {
+ strings[i] = String.format("%04x", shorts[i]);
+ }
+ return strings;
+ }
+
+ private short[] toShortArray()
+ {
+ final short[] shorts = new short[N_SHORTS];
+
+ for (int i = 0; i < N_SHORTS; i++)
+ {
+ if (IPv6AddressHelpers.inHighRange(i))
+ shorts[i] = (short) (((highBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ else
+ shorts[i] = (short) (((lowBits << i * 16) >>> 16 * (N_SHORTS - 1)) & 0xFFFF);
+ }
+
+ return shorts;
+ }
+
+ int[] startAndLengthOfLongestRunOfZeroes()
+ {
+ int longestConsecutiveZeroes = 0;
+ int longestConsecutiveZeroesPos = -1;
+ short[] shorts = toShortArray();
+ for (int pos = 0; pos < shorts.length; pos++)
+ {
+ int consecutiveZeroesAtCurrentPos = countConsecutiveZeroes(shorts, pos);
+ if (consecutiveZeroesAtCurrentPos > longestConsecutiveZeroes)
+ {
+ longestConsecutiveZeroes = consecutiveZeroesAtCurrentPos;
+ longestConsecutiveZeroesPos = pos;
+ }
+ }
+
+ return new int[]{longestConsecutiveZeroesPos, longestConsecutiveZeroes};
+ }
+
+ private int countConsecutiveZeroes(short[] shorts, int offset)
+ {
+ int count = 0;
+ for (int i = offset; i < shorts.length && shorts[i] == 0; i++)
+ {
+ count++;
+ }
+
+ return count;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6Address that = (IPv6Address) o;
+
+ if (highBits != that.highBits) return false;
+ if (lowBits != that.lowBits) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = (int) (lowBits ^ (lowBits >>> 32));
+ result = 31 * result + (int) (highBits ^ (highBits >>> 32));
+ return result;
+ }
+
+ public int compareTo(IPv6Address that)
+ {
+ if (this.highBits == that.highBits)
+ if (this.lowBits == that.lowBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.lowBits, that.lowBits) ? -1 : 1;
+ else if (this.highBits == that.highBits)
+ return 0;
+ else
+ return IPv6AddressHelpers.isLessThanUnsigned(this.highBits, that.highBits) ? -1 : 1;
+ }
+
+ public long getHighBits()
+ {
+ return highBits;
+ }
+
+ public long getLowBits()
+ {
+ return lowBits;
+ }
+
+ public int numberOfTrailingZeroes()
+ {
+ return lowBits == 0 ?
+ Long.numberOfTrailingZeros(highBits) + 64 :
+ Long.numberOfTrailingZeros(lowBits);
+ }
+
+ public int numberOfTrailingOnes()
+ {
+ // count trailing ones in "value" by counting the trailing zeroes in "value + 1"
+ final IPv6Address plusOne = this.add(1);
+ return plusOne.getLowBits() == 0 ?
+ Long.numberOfTrailingZeros(plusOne.getHighBits()) + 64 :
+ Long.numberOfTrailingZeros(plusOne.getLowBits());
+ }
+
+ public int numberOfLeadingZeroes()
+ {
+ return highBits == 0 ?
+ Long.numberOfLeadingZeros(lowBits) + 64 :
+ Long.numberOfLeadingZeros(highBits);
+ }
+
+ public int numberOfLeadingOnes()
+ {
+ // count leading ones in "value" by counting leading zeroes in "~ value"
+ final IPv6Address flipped = new IPv6Address(~this.highBits, ~this.lowBits);
+ return flipped.numberOfLeadingZeroes();
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
new file mode 100644
index 0000000..8403cfe
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressHelpers.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+/**
+ * Helper methods used by IPv6Address.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressHelpers
+{
+ static long[] parseStringArrayIntoLongArray(String[] strings)
+ {
+ final long[] longs = new long[strings.length];
+ for (int i = 0; i < strings.length; i++)
+ {
+ longs[i] = Long.parseLong(strings[i], 16);
+ }
+ return longs;
+ }
+
+ static void validateLongs(long[] longs)
+ {
+ if (longs.length != 8)
+ throw new IllegalArgumentException("an IPv6 address should contain 8 shorts [" + Arrays.toString(longs) + "]");
+
+ for (long l : longs)
+ {
+ if (l < 0) throw new IllegalArgumentException("each element should be positive [" + Arrays.toString(longs) + "]");
+ if (l > 0xFFFF) throw new IllegalArgumentException("each element should be less than 0xFFFF [" + Arrays.toString(longs) + "]");
+ }
+ }
+
+ static IPv6Address mergeLongArrayIntoIPv6Address(long[] longs)
+ {
+ long high = 0L;
+ long low = 0L;
+
+ for (int i = 0; i < longs.length; i++)
+ {
+ if (inHighRange(i))
+ high |= (longs[i] << ((longs.length - i - 1) * 16));
+ else
+ low |= (longs[i] << ((longs.length - i - 1) * 16));
+ }
+
+ return new IPv6Address(high, low);
+ }
+
+ static boolean inHighRange(int shortNumber)
+ {
+ return shortNumber >= 0 && shortNumber < 4;
+ }
+
+ static String expandShortNotation(String string)
+ {
+ if (!string.contains("::"))
+ {
+ return string;
+ }
+ else if (string.equals("::"))
+ {
+ return generateZeroes(8);
+ }
+ else
+ {
+ final int numberOfColons = countOccurrences(string, ':');
+ if (string.startsWith("::"))
+ return string.replace("::", generateZeroes((7 + 2) - numberOfColons));
+ else if (string.endsWith("::"))
+ return string.replace("::", ":" + generateZeroes((7 + 2) - numberOfColons));
+ else
+ return string.replace("::", ":" + generateZeroes((7 + 2 - 1) - numberOfColons));
+ }
+ }
+
+ private static final Pattern DOT_DELIM = Pattern.compile("\\.");
+
+ /**
+ * Replaces a w.x.y.z substring at the end of the given string with corresponding hexadecimal notation. This is useful in case the
+ * string was using IPv4-Mapped address notation.
+ */
+ static String rewriteIPv4MappedNotation(String string)
+ {
+ if (!string.contains("."))
+ {
+ return string;
+ }
+ else
+ {
+ int lastColon = string.lastIndexOf(":");
+ String firstPart = string.substring(0, lastColon + 1);
+ String mappedIPv4Part = string.substring(lastColon + 1);
+
+ if (mappedIPv4Part.contains("."))
+ {
+ String[] dotSplits = DOT_DELIM.split(mappedIPv4Part);
+ if (dotSplits.length != 4)
+ throw new IllegalArgumentException(String.format("can not parse [%s]", string));
+
+ StringBuilder rewrittenString = new StringBuilder();
+ rewrittenString.append(firstPart);
+ int byteZero = Integer.parseInt(dotSplits[0]);
+ int byteOne = Integer.parseInt(dotSplits[1]);
+ int byteTwo = Integer.parseInt(dotSplits[2]);
+ int byteThree = Integer.parseInt(dotSplits[3]);
+
+ rewrittenString.append(String.format("%02x", byteZero));
+ rewrittenString.append(String.format("%02x", byteOne));
+ rewrittenString.append(":");
+ rewrittenString.append(String.format("%02x", byteTwo));
+ rewrittenString.append(String.format("%02x", byteThree));
+
+ return rewrittenString.toString();
+ }
+ else
+ {
+ throw new IllegalArgumentException(String.format("can not parse [%s]", string));
+ }
+ }
+ }
+
+ public static int countOccurrences(String haystack, char needle)
+ {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++)
+ {
+ if (haystack.charAt(i) == needle)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static String generateZeroes(int number)
+ {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number; i++)
+ {
+ builder.append("0:");
+ }
+
+ return builder.toString();
+ }
+
+ static boolean isLessThanUnsigned(long a, long b)
+ {
+ return (a < b) ^ ((a < 0) != (b < 0));
+ }
+
+ static byte[] prefixWithZeroBytes(byte[] original, int newSize)
+ {
+ byte[] target = new byte[newSize];
+ System.arraycopy(original, 0, target, newSize - original.length, original.length);
+ return target;
+ }
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
new file mode 100644
index 0000000..77b9a0f
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressPool.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+
+import java.util.*;
+
+/**
+ * Immutable representation of an IPv6 address pool.
+ *
+ * An IPv6 address pool is like an IPv6 address range in which some addresses are "free" and some are "allocated". Think "dhcp server".
+ * Addresses are allocated in whole subnet blocks at once. These subnet blocks have a predefined prefix length for the whole allocatable
+ * range.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6AddressPool
+{
+ private final IPv6AddressRange underlyingRange;
+
+ private final SortedSet freeRanges;
+
+ private final IPv6NetworkMask allocationSubnetSize;
+
+ private final IPv6Network lastAllocated;
+
+ /**
+ * Create a pool of the given range (boundaries inclusive) which is completely free. The given subnet size is the network mask (thus
+ * size) of the allocated subnets in this range. This constructor verifies that the whole range is "aligned" with subnets of this size
+ * (i.e. there should not be a waste of space in the beginning or end which is smaller than one subnet of the given subnet size).
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ */
+ public static IPv6AddressPool fromRangeAndSubnet(final IPv6AddressRange range,
+ final IPv6NetworkMask allocationSubnetSize)
+ {
+ // in the beginning, all is free
+ return new IPv6AddressPool(range, allocationSubnetSize, new TreeSet(Arrays.asList(range)), null);
+ }
+
+ /**
+ * Private constructor to construct a pool with a given set of free ranges and a network which was just allocated.
+ *
+ * @param range range from within to allocate
+ * @param allocationSubnetSize size of the subnets that will be allocated
+ * @param freeRanges free ranges in the allocatable IP address range
+ */
+ private IPv6AddressPool(final IPv6AddressRange range, final IPv6NetworkMask allocationSubnetSize,
+ final SortedSet freeRanges, final IPv6Network lastAllocated)
+ {
+ this.underlyingRange = range;
+
+ this.allocationSubnetSize = allocationSubnetSize;
+ this.freeRanges = Collections.unmodifiableSortedSet(freeRanges);
+ this.lastAllocated = lastAllocated;
+
+ validateFreeRanges(underlyingRange, freeRanges);
+ validateRangeIsMultipleOfSubnetsOfGivenSize(underlyingRange, allocationSubnetSize);
+ }
+
+ private void validateFreeRanges(IPv6AddressRange range, SortedSet toValidate)
+ {
+ if (!toValidate.isEmpty() && !checkWithinBounds(range, toValidate))
+ throw new IllegalArgumentException("invalid free ranges: not all within bounds of overall range");
+
+ // TODO: some more validations would be useful. For example the free ranges should be defragmented and non overlapping etc
+ }
+
+ private boolean checkWithinBounds(IPv6AddressRange range, SortedSet toValidate)
+ {
+ return (toValidate.first().getFirst().compareTo(range.getFirst()) >= 0
+ && toValidate.last().getLast().compareTo(range.getLast()) <= 0);
+ }
+
+ private void validateRangeIsMultipleOfSubnetsOfGivenSize(IPv6AddressRange range, IPv6NetworkMask allocationSubnetSize)
+ {
+ final int allocatableBits = 128 - allocationSubnetSize.asPrefixLength();
+
+ if (range.getFirst().numberOfTrailingZeroes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength() + "], "
+ + "first address should end with " +
+ allocatableBits + " zero bits");
+
+ if (range.getLast().numberOfTrailingOnes() < allocatableBits)
+ throw new IllegalArgumentException(
+ "range [" + this + "] is not aligned with prefix length [" + allocationSubnetSize.asPrefixLength()
+ + "], last address should end with " +
+ allocatableBits + " one bits");
+ }
+
+ /**
+ * @return the last IPv6Network which was allocated or null if none was allocated yet
+ */
+ public IPv6Network getLastAllocated()
+ {
+ return lastAllocated;
+ }
+
+ /**
+ * Allocate the first available subnet from the pool.
+ *
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate()
+ {
+ if (!isExhausted())
+ {
+ // get the first range of free subnets, and take the first subnet of that range
+ final IPv6AddressRange firstFreeRange = freeRanges.first();
+ final IPv6Network allocated = IPv6Network.fromAddressAndMask(firstFreeRange.getFirst(), allocationSubnetSize);
+
+ return doAllocate(allocated, firstFreeRange);
+ }
+ else
+ {
+ // exhausted
+ return null;
+ }
+ }
+
+ /**
+ * Allocate the given subnet from the pool.
+ *
+ * @param toAllocate subnet to allocate from the pool
+ * @return resulting pool
+ */
+ public IPv6AddressPool allocate(IPv6Network toAllocate)
+ {
+ if (!contains(toAllocate))
+ throw new IllegalArgumentException(
+ "can not allocate network which is not contained in the pool to allocate from [" + toAllocate + "]");
+
+ if (!this.allocationSubnetSize.equals(toAllocate.getNetmask()))
+ throw new IllegalArgumentException("can not allocate network with prefix length /" + toAllocate.getNetmask().asPrefixLength() +
+ " from a pool configured to hand out subnets with prefix length /"
+ + allocationSubnetSize);
+
+ // go find the range that contains the requested subnet
+ final IPv6AddressRange rangeToAllocateFrom = findFreeRangeContaining(toAllocate);
+
+ if (rangeToAllocateFrom != null)
+ {
+ // found a range in which this subnet is free, allocate it
+ return doAllocate(toAllocate, rangeToAllocateFrom);
+ }
+ else
+ {
+ // requested subnet not free
+ return null;
+ }
+ }
+
+ private IPv6AddressRange findFreeRangeContaining(IPv6Network toAllocate)
+ {
+ // split around the subnet to allocate
+ final SortedSet head = freeRanges.headSet(toAllocate);
+ final SortedSet tail = freeRanges.tailSet(toAllocate);
+
+ // the range containing the network to allocate is either the first of the tail, or the last of the head, or it doesn't exist
+ if (!head.isEmpty() && head.last().contains(toAllocate))
+ {
+ return head.last();
+ }
+ else if (!tail.isEmpty() && tail.first().contains(toAllocate))
+ {
+ return tail.first();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Private helper method to perform the allocation of a subnet within one of the free ranges.
+ *
+ * @param toAllocate subnet to allocate
+ * @param rangeToAllocateFrom free range to allocate from
+ * @return resulting pool
+ */
+ private IPv6AddressPool doAllocate(final IPv6Network toAllocate, final IPv6AddressRange rangeToAllocateFrom)
+ {
+ assert freeRanges.contains(rangeToAllocateFrom);
+ assert rangeToAllocateFrom.contains(toAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ // remove range from free ranges
+ newFreeRanges.remove(rangeToAllocateFrom);
+
+ // from the range, remove the allocated subnet
+ final List newRanges = rangeToAllocateFrom.remove(toAllocate);
+
+ // and add the resulting ranges as new free ranges
+ newFreeRanges.addAll(newRanges);
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, toAllocate);
+ }
+
+ /**
+ * Give a network back to the pool (de-allocate).
+ *
+ * @param toDeAllocate network to de-allocate
+ */
+ public IPv6AddressPool deAllocate(final IPv6Network toDeAllocate)
+ {
+ if (!contains(toDeAllocate))
+ {
+ throw new IllegalArgumentException(
+ "Network to de-allocate[" + toDeAllocate + "] is not contained in this allocatable range [" + this + "]");
+ }
+
+ // find ranges just in front or after the network to deallocate. These are the ranges to merge with to prevent fragmentation.
+ final IPv6AddressRange freeRangeBeforeNetwork = findFreeRangeBefore(toDeAllocate);
+ final IPv6AddressRange freeRangeAfterNetwork = findFreeRangeAfter(toDeAllocate);
+
+ final TreeSet newFreeRanges = new TreeSet(this.freeRanges);
+
+ if ((freeRangeBeforeNetwork == null) && (freeRangeAfterNetwork == null))
+ {
+ // nothing to "defragment"
+ newFreeRanges.add(toDeAllocate);
+ }
+ else
+ {
+ if ((freeRangeBeforeNetwork != null) && (freeRangeAfterNetwork != null))
+ {
+ // merge two existing ranges
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ else if (freeRangeBeforeNetwork != null)
+ {
+ // append
+ newFreeRanges.remove(freeRangeBeforeNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(freeRangeBeforeNetwork.getFirst(), toDeAllocate.getLast()));
+ }
+ else /*if (freeRangeAfterNetwork != null)*/
+ {
+ // prepend
+ newFreeRanges.remove(freeRangeAfterNetwork);
+ newFreeRanges.add(IPv6AddressRange.fromFirstAndLast(toDeAllocate.getFirst(), freeRangeAfterNetwork.getLast()));
+ }
+ }
+
+ return new IPv6AddressPool(underlyingRange, allocationSubnetSize, newFreeRanges, getLastAllocated());
+ }
+
+ /**
+ * Private helper method to find the free range just before the given network.
+ */
+ private IPv6AddressRange findFreeRangeBefore(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getLast().add(1).equals(network.getFirst()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * Private helper method to find the free range just after the given address.
+ */
+ private IPv6AddressRange findFreeRangeAfter(IPv6Network network)
+ {
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.getFirst().subtract(1).equals(network.getLast()))
+ {
+ return freeRange;
+ }
+ }
+
+ // not found
+ return null;
+ }
+
+ /**
+ * @return true if no subnets are free in this pool, false otherwize
+ */
+ public boolean isExhausted()
+ {
+ return freeRanges.isEmpty();
+ }
+
+ public boolean isFree(final IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("network invalid [null]");
+
+ if (!this.allocationSubnetSize.equals(network.getNetmask()))
+ throw new IllegalArgumentException(
+ "network of prefix length [" + network.getNetmask().asPrefixLength()
+ + "] can not be free in a pool which uses prefix length [" +
+ allocationSubnetSize + "]");
+
+ // find a free range that contains the network
+ for (IPv6AddressRange freeRange : freeRanges)
+ {
+ if (freeRange.contains(network))
+ {
+ return true;
+ }
+ }
+
+ // nothing found
+ return false;
+ }
+
+ /**
+ * @return all networks (all with the same fixed prefix length) which are free in this pool
+ */
+ public Iterable freeNetworks()
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /*
+ * Iteration is implemented by allocating from a separate pool.
+ */
+
+ private IPv6AddressPool poolInstanceUsedForIteration = IPv6AddressPool.this;
+
+ @Override
+ public boolean hasNext()
+ {
+ return !poolInstanceUsedForIteration.isExhausted();
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ poolInstanceUsedForIteration = poolInstanceUsedForIteration.allocate();
+ return poolInstanceUsedForIteration.lastAllocated;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove not supported");
+ }
+ };
+ }
+ };
+ }
+
+// /**
+// * @return all networks (all with the same fixed prefix length) which are allocated in this pool
+// */
+// public Iterable allocatedNetworks()
+// {
+// return new Iterable()
+// {
+// @Override
+// public Iterator iterator()
+// {
+// return new Iterator()
+// {
+// @Override
+// public boolean hasNext()
+// {
+// throw new UnsupportedOperationException("TODO: implement hasNext");
+// }
+//
+// @Override
+// public IPv6Network next()
+// {
+// throw new UnsupportedOperationException("TODO: implement next");
+// }
+//
+// @Override
+// public void remove()
+// {
+// throw new UnsupportedOperationException("TODO: implement remove");
+// }
+// };
+// }
+// };
+// }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6AddressPool that = (IPv6AddressPool) o;
+
+ if (allocationSubnetSize != null ? !allocationSubnetSize.equals(that.allocationSubnetSize) : that.allocationSubnetSize != null)
+ return false;
+ if (freeRanges != null ? !freeRanges.equals(that.freeRanges) : that.freeRanges != null) return false;
+ if (lastAllocated != null ? !lastAllocated.equals(that.lastAllocated) : that.lastAllocated != null) return false;
+ if (underlyingRange != null ? !underlyingRange.equals(that.underlyingRange) : that.underlyingRange != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = underlyingRange != null ? underlyingRange.hashCode() : 0;
+ result = 31 * result + (freeRanges != null ? freeRanges.hashCode() : 0);
+ result = 31 * result + (allocationSubnetSize != null ? allocationSubnetSize.hashCode() : 0);
+ result = 31 * result + (lastAllocated != null ? lastAllocated.hashCode() : 0);
+ return result;
+ }
+
+
+ // delegation methods
+
+ public boolean contains(IPv6Address address)
+ {
+ return underlyingRange.contains(address);
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return underlyingRange.contains(range);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return underlyingRange.overlaps(range);
+ }
+
+ public IPv6Address getFirst()
+ {
+ return underlyingRange.getFirst();
+ }
+
+ public IPv6Address getLast()
+ {
+ return underlyingRange.getLast();
+ }
+
+ @Override
+ public String toString()
+ {
+ return underlyingRange.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return underlyingRange.toLongString();
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
new file mode 100644
index 0000000..0cd612b
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6AddressRange.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * Immutable representation of a continuous range of IPv6 addresses (bounds included).
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRange implements Comparable, Iterable
+{
+ private final IPv6Address first;
+
+ private final IPv6Address last;
+
+ IPv6AddressRange(IPv6Address first, IPv6Address last)
+ {
+ if (first.compareTo(last) > 0)
+ throw new IllegalArgumentException("Cannot create ip address range with last address < first address");
+
+ this.first = first;
+ this.last = last;
+ }
+
+ public static IPv6AddressRange fromFirstAndLast(IPv6Address first, IPv6Address last)
+ {
+ return new IPv6AddressRange(first, last);
+ }
+
+ public boolean contains(IPv6Address address)
+ {
+ return first.compareTo(address) <= 0 && last.compareTo(address) >= 0;
+ }
+
+ public boolean contains(IPv6AddressRange range)
+ {
+ return contains(range.first) && contains(range.last);
+ }
+
+ public boolean overlaps(IPv6AddressRange range)
+ {
+ return contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last);
+ }
+
+ /**
+ * @return an iterator which iterates all addresses in this range, in order.
+ */
+ @Override
+ public Iterator iterator()
+ {
+ return new Ipv6AddressRangeIterator();
+ }
+
+ public BigInteger size()
+ {
+ BigInteger firstAsBigInteger = new BigInteger(1, first.toByteArray());
+ BigInteger lastAsBigInteger = new BigInteger(1, last.toByteArray());
+
+ // note that first and last are included in the range.
+ return lastAsBigInteger.subtract(firstAsBigInteger).add(BigInteger.ONE);
+ }
+
+ /**
+ * Remove an address from the range, resulting in one, none or two new ranges. If an address outside the range is removed, this has no
+ * effect. If the first or last address is removed, a single new range is returned (potentially empty if the range only contained a
+ * single address). If an address somewhere else in the range is removed, two new ranges are returned.
+ *
+ * @param address adddress to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Address address)
+ {
+ if (address == null)
+ throw new IllegalArgumentException("invalid address [null]");
+
+ if (!contains(address))
+ return Collections.singletonList(this);
+ else if (address.equals(first) && address.equals(last))
+ return Collections.emptyList();
+ else if (address.equals(first))
+ return Collections.singletonList(fromFirstAndLast(first.add(1), last));
+ else if (address.equals(last))
+ return Collections.singletonList(fromFirstAndLast(first, last.subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, address.subtract(1)),
+ fromFirstAndLast(address.add(1), last));
+ }
+
+ /**
+ * Extend the range just enough at its head or tail such that the given address is included.
+ *
+ * @param address address to extend the range to
+ * @return new (bigger) range
+ */
+ public IPv6AddressRange extend(IPv6Address address)
+ {
+ if (address.compareTo(first) < 0)
+ return fromFirstAndLast(address, last);
+ else if (address.compareTo(last) > 0)
+ return fromFirstAndLast(first, address);
+ else
+ return this;
+ }
+
+ /**
+ * Remove a network from the range, resulting in one, none or two new ranges. If a network outside (or partially outside) the range is
+ * removed, this has no effect. If the network which is removed is aligned with the beginning or end of the range, a single new ranges
+ * is returned (potentially empty if the range was equal to the network which is removed from it). If a network somewhere else in the
+ * range is removed, two new ranges are returned.
+ *
+ * @param network network to remove from the range
+ * @return list of resulting ranges
+ */
+ public List remove(IPv6Network network)
+ {
+ if (network == null)
+ throw new IllegalArgumentException("invalid network [null]");
+
+ if (!contains(network))
+ return Collections.singletonList(this);
+ else if (this.equals(network))
+ return Collections.emptyList();
+ else if (first.equals(network.getFirst()))
+ return Collections.singletonList(fromFirstAndLast(network.getLast().add(1), last));
+ else if (last.equals(network.getLast()))
+ return Collections.singletonList(fromFirstAndLast(first, network.getFirst().subtract(1)));
+ else
+ return Arrays.asList(fromFirstAndLast(first, network.getFirst().subtract(1)),
+ fromFirstAndLast(network.getLast().add(1), last));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return first.toString() + " - " + last.toString();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return first.toLongString() + " - " + last.toLongString();
+ }
+
+ /**
+ * The natural order of {@link com.googlecode.ipv6.IPv6AddressRange}s orders them on increasing first addresses, and on increasing last
+ * address if the first address would be equal.
+ *
+ * Note that the natural order does thus not compare sizes of ranges.
+ *
+ * @param that range to compare with
+ * @return negative, zero or positive depending on whether this is smaller, equal or greater than that
+ */
+ @Override
+ public int compareTo(IPv6AddressRange that)
+ {
+ if (!this.first.equals(that.first))
+ return this.first.compareTo(that.first);
+ else
+ return this.last.compareTo(that.last);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (!(o instanceof IPv6AddressRange)) return false;
+
+ IPv6AddressRange that = (IPv6AddressRange) o;
+
+ if (first != null ? !first.equals(that.first) : that.first != null) return false;
+ if (last != null ? !last.equals(that.last) : that.last != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = first != null ? first.hashCode() : 0;
+ result = 31 * result + (last != null ? last.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6Address getFirst()
+ {
+ return first;
+ }
+
+ public IPv6Address getLast()
+ {
+ return last;
+ }
+
+ /**
+ * @see IPv6AddressRange#iterator()
+ */
+ private final class Ipv6AddressRangeIterator implements Iterator
+ {
+ private IPv6Address current = first;
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.compareTo(last) <= 0;
+ }
+
+ @Override
+ public IPv6Address next()
+ {
+ if (hasNext())
+ {
+ IPv6Address result = current;
+ current = current.add(1);
+ return result;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("This iterator provides read only access");
+ }
+ }
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6Network.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6Network.java
new file mode 100644
index 0000000..f40c5d0
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6Network.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Immutable representation of an IPv6 network based on an address and a prefix length. An IPv6 network is also an IPv6 address range (but
+ * not all ranges are valid networks).
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6Network extends IPv6AddressRange
+{
+ public static final IPv6Network MULTICAST_NETWORK = fromString("ff00::/8");
+
+ public static final IPv6Network SITE_LOCAL_NETWORK = fromString("fec0::/48");
+
+ public static final IPv6Network LINK_LOCAL_NETWORK = fromString("fe80::/64");
+
+
+ private final IPv6Address address;
+
+ private final IPv6NetworkMask networkMask;
+
+ /**
+ * Construct from address and network mask.
+ *
+ * @param address address
+ * @param networkMask network mask
+ */
+ private IPv6Network(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ super(address.maskWithNetworkMask(networkMask), address.maximumAddressWithNetworkMask(networkMask));
+
+ this.address = address.maskWithNetworkMask(networkMask);
+ this.networkMask = networkMask;
+ }
+
+ /**
+ * Create an IPv6 network from an IPv6Address and an IPv6NetworkMask
+ *
+ * @param address IPv6 address (the network address or any other address within the network)
+ * @param networkMask IPv6 network mask
+ * @return IPv6 network
+ */
+ public static IPv6Network fromAddressAndMask(IPv6Address address, IPv6NetworkMask networkMask)
+ {
+ return new IPv6Network(address, networkMask);
+ }
+
+ /**
+ * Create an IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix
+ * length") which contains both addresses.
+ *
+ * @param one address one
+ * @param two address two, should be bigger than address one
+ */
+ public static IPv6Network fromTwoAddresses(IPv6Address one, IPv6Address two)
+ {
+ final IPv6NetworkMask longestPrefixLength = IPv6NetworkMask.fromPrefixLength(IPv6NetworkHelpers.longestPrefixLength(one, two));
+ return new IPv6Network(one.maskWithNetworkMask(longestPrefixLength), longestPrefixLength);
+ }
+
+ /**
+ * Create an IPv6 network from its String representation. For example "1234:5678:abcd:0:0:0:0:0/64" or "2001::ff/128".
+ *
+ * @param string string representation
+ * @return IPv6 network
+ */
+ public static IPv6Network fromString(String string)
+ {
+ if (string.indexOf('/') == -1)
+ {
+ throw new IllegalArgumentException("Expected format is network-address/prefix-length");
+ }
+
+ final String networkAddressString = parseNetworkAddress(string);
+ int prefixLength = parsePrefixLength(string);
+
+ final IPv6Address networkAddress = IPv6Address.fromString(networkAddressString);
+
+ return fromAddressAndMask(networkAddress, new IPv6NetworkMask(prefixLength));
+ }
+
+ private static String parseNetworkAddress(String string)
+ {
+ return string.substring(0, string.indexOf('/'));
+ }
+
+ private static int parsePrefixLength(String string)
+ {
+ try
+ {
+ return Integer.parseInt(string.substring(string.indexOf('/') + 1));
+ } catch (NumberFormatException e)
+ {
+ throw new IllegalArgumentException("Prefix length should be a positive integer");
+ }
+ }
+
+ /**
+ * Split a network in smaller subnets of a given size.
+ *
+ * @param size size (expressed as {@link com.googlecode.ipv6.IPv6NetworkMask}) of the subnets
+ * @return iterator of the splitted subnets.
+ * @throws IllegalArgumentException if the requested size is bigger than the original size
+ */
+ public Iterator split(IPv6NetworkMask size)
+ {
+ if (size.asPrefixLength() < this.getNetmask().asPrefixLength())
+ throw new IllegalArgumentException(String.format("Can not split a network of size %s in subnets of larger size %s",
+ this.getNetmask().asPrefixLength(), size.asPrefixLength()));
+
+ return new IPv6NetworkSplitsIterator(size);
+ }
+
+ @Override
+ public String toString()
+ {
+ return address.toString() + "/" + networkMask.asPrefixLength();
+ }
+
+ /**
+ * @return like toString but without using shorthand notations for addresses
+ */
+ public String toLongString()
+ {
+ return address.toLongString() + "/" + networkMask.asPrefixLength();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ IPv6Network that = (IPv6Network) o;
+
+ if (address != null ? !address.equals(that.address) : that.address != null) return false;
+ if (networkMask != null ? !networkMask.equals(that.networkMask) : that.networkMask != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = super.hashCode();
+ result = 31 * result + (address != null ? address.hashCode() : 0);
+ result = 31 * result + (networkMask != null ? networkMask.hashCode() : 0);
+ return result;
+ }
+
+ public IPv6NetworkMask getNetmask()
+ {
+ return networkMask;
+ }
+
+ private final class IPv6NetworkSplitsIterator implements Iterator
+ {
+ private final IPv6NetworkMask size;
+
+ private IPv6Network current;
+
+ private BigInteger nbrAddressesPerSplit;
+
+ public IPv6NetworkSplitsIterator(IPv6NetworkMask size)
+ {
+ this.size = size;
+ this.nbrAddressesPerSplit = BigInteger.ONE.shiftLeft(128 - size.asPrefixLength());
+ this.current = IPv6Network.fromAddressAndMask(IPv6Network.this.address, size);
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return current.getLast().compareTo(IPv6Network.this.getLast()) <= 0;
+ }
+
+ @Override
+ public IPv6Network next()
+ {
+ if (hasNext())
+ {
+ IPv6Network result = current;
+ current = calculateNext(current);
+ return result;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ private IPv6Network calculateNext(IPv6Network current)
+ {
+ BigInteger next = current.address.toBigInteger().add(nbrAddressesPerSplit);
+ return IPv6Network.fromAddressAndMask(IPv6Address.fromBigInteger(next), size);
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("This iterator provides read only access");
+ }
+ }
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
new file mode 100644
index 0000000..66268ed
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6NetworkHelpers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Helper methods used by IPv6Network.
+ *
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpers
+{
+ static int longestPrefixLength(IPv6Address first, IPv6Address last)
+ {
+ final BitSet firstBits = bitSetOf(first.getLowBits(), first.getHighBits());
+ final BitSet lastBits = bitSetOf(last.getLowBits(), last.getHighBits());
+
+ return countLeadingSimilarBits(firstBits, lastBits);
+ }
+
+ private static int countLeadingSimilarBits(BitSet a, BitSet b)
+ {
+ int result = 0;
+ for (int i = 127; i >= 0 && (a.get(i) == b.get(i)); i--)
+ {
+ result++;
+ }
+
+ return result;
+ }
+}
diff --git a/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
new file mode 100644
index 0000000..ee4394b
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/main/java/com/googlecode/ipv6/IPv6NetworkMask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import java.util.BitSet;
+
+import static com.googlecode.ipv6.BitSetHelpers.bitSetOf;
+
+/**
+ * Immutable representation of an IPv6 network mask. A network mask is nothing more than an IPv6 address with a continuous range of 1 bits
+ * starting from the most significant bit. A network mask can also be represented as a prefix length, which is the count of these 1 bits.
+ *
+ * @author Jan Van Besien
+ */
+public final class IPv6NetworkMask
+{
+ private final int prefixLength;
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ IPv6NetworkMask(int prefixLength)
+ {
+ if (prefixLength < 0 || prefixLength > 128)
+ throw new IllegalArgumentException("prefix length should be in interval [0, 128]");
+
+ this.prefixLength = prefixLength;
+ }
+
+
+ /**
+ * Construct an IPv6 network mask from an IPv6 address. The address should be a valid network mask.
+ *
+ * @param iPv6Address address to use as network mask
+ * @throws IllegalArgumentException if the address is not a valid network mask
+ */
+ public static IPv6NetworkMask fromAddress(final IPv6Address iPv6Address)
+ {
+ validateNetworkMask(iPv6Address);
+ return new IPv6NetworkMask(iPv6Address.numberOfLeadingOnes());
+ }
+
+ /**
+ * Construct an IPv6 network mask from a prefix length. The prefix length should be in the interval ]0, 128].
+ *
+ * @param prefixLength prefix length
+ * @throws IllegalArgumentException if the prefix length is not in the interval ]0, 128]
+ */
+ public static IPv6NetworkMask fromPrefixLength(int prefixLength)
+ {
+ return new IPv6NetworkMask(prefixLength);
+ }
+
+ private static void validateNetworkMask(IPv6Address addressToValidate)
+ {
+ final BitSet addressAsBitSet = bitSetOf(addressToValidate.getLowBits(), addressToValidate.getHighBits());
+ if (!addressAsBitSet.get(127))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ else
+ {
+ boolean firstZeroFound = false;
+ for (int i = 127; i >= 0 && !firstZeroFound; i--)
+ {
+ if (!addressAsBitSet.get(i))
+ {
+ firstZeroFound = true;
+
+ // a zero -> all the others should also be zero
+ for (int j = i - 1; j >= 0; j--)
+ {
+ if (addressAsBitSet.get(j))
+ {
+ throw new IllegalArgumentException(addressToValidate + " is not a valid network mask");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int asPrefixLength()
+ {
+ return prefixLength;
+ }
+
+ public IPv6Address asAddress()
+ {
+ if (prefixLength == 128)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
+ }
+ else if (prefixLength == 64)
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, 0L);
+ }
+ else if (prefixLength > 64)
+ {
+ final int remainingPrefixLength = prefixLength - 64;
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL, (0xFFFFFFFFFFFFFFFFL << (64 - remainingPrefixLength)));
+ }
+ else
+ {
+ return new IPv6Address(0xFFFFFFFFFFFFFFFFL << (64 - prefixLength), 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IPv6NetworkMask that = (IPv6NetworkMask) o;
+
+ if (prefixLength != that.prefixLength) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return prefixLength;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "" + prefixLength;
+ }
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
new file mode 100644
index 0000000..834d561
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressPoolTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressPoolTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedStart()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::1"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructUnalignedEnd()
+ {
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:fffe")),
+ new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void constructAligned()
+ {
+ // all these are correctly aligned with the given prefix length, so none should throw exception
+
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::ab00"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2000:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("2001::ffff:ffff")), new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::0"), fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::abcd:ef00"),
+ fromString("2001::abcd:efff")), new IPv6NetworkMask(120));
+ }
+
+ @Test
+ public void autoAllocateAndDeallocateSingle128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null", pool.allocate());
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertFalse(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate();
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void autoAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void manuallyAllocateSingle128Available()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128)));
+
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ assertTrue(pool.isExhausted());
+
+ assertNull("allocation in exhausted range returns null",
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::1"), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void manuallyAllocateSingle128OutOfRange()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::1")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ pool.allocate(IPv6Network.fromAddressAndMask(fromString("::99"), IPv6NetworkMask.fromPrefixLength(128)));
+ }
+
+ @Test
+ public void manuallyAllocateMultiple128()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::1"),
+ fromString("::5")),
+ new IPv6NetworkMask(128));
+ assertFalse(pool.isExhausted());
+
+ for (int i = 1; i <= 5; i++)
+ {
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("::" + i), IPv6NetworkMask.fromPrefixLength(128))));
+ }
+
+ assertTrue(pool.isExhausted());
+ }
+
+ @Test
+ public void manuallyAllocateAFew120s()
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("2001::"),
+ fromString("2001::ffff:ffff")),
+ new IPv6NetworkMask(120));
+ assertFalse(pool.isExhausted());
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120)), pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.allocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)),
+ pool.getLastAllocated());
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ assertFalse(pool.isExhausted());
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::ffff:0"), IPv6NetworkMask.fromPrefixLength(120))));
+
+ pool = pool.deAllocate(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120)));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertTrue(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::100"), IPv6NetworkMask.fromPrefixLength(120))));
+ assertFalse(pool.isFree(IPv6Network.fromAddressAndMask(fromString("2001::200"), IPv6NetworkMask.fromPrefixLength(120))));
+ }
+
+ @Test
+ public void allocateOnBoundariesLowBits()
+ {
+ for (int i = 64; i > 0; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void allocateOnBoundariesHighBits()
+ {
+ for (int i = 128; i > 64; i--)
+ {
+ IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(i));
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ pool = pool.allocate();
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::").maximumAddressWithNetworkMask(new IPv6NetworkMask(i)).add(1),
+ IPv6NetworkMask.fromPrefixLength(i)), pool.getLastAllocated());
+ }
+ }
+
+ @Test
+ public void iterateFreeNetworks()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(IPv6AddressRange.fromFirstAndLast(fromString("::"),
+ fromString(
+ "::ffff:ffff:ffff:ffff")),
+ new IPv6NetworkMask(66));
+ final Set freeNetworks = new HashSet();
+ for (IPv6Network network : pool.freeNetworks())
+ {
+ freeNetworks.add(network);
+ }
+
+ assertEquals(4, freeNetworks.size());
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::4000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::8000:0:0:0/66")));
+ assertTrue(freeNetworks.contains(IPv6Network.fromString("::c000:0:0:0/66")));
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
new file mode 100644
index 0000000..9cd1473
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressRangeTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static com.googlecode.ipv6.IPv6AddressRange.fromFirstAndLast;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressRangeTest
+{
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalid()
+ {
+ fromFirstAndLast(fromString("::2"), fromString("::1"));
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:9:8:7")));
+ assertTrue(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::5:6:7:8")));
+ assertTrue(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:2:3:4")));
+
+ assertTrue(fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:12:11:10:9:8")));
+ assertTrue(fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:2:3:4:5:6:7:8")));
+ assertTrue(fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("9:10:11:12:13:14:15:16")));
+ }
+
+ @Test
+ public void doesNotContain()
+ {
+ assertFalse(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::9:9:9:9")));
+ assertFalse(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).contains(fromString("::1:1:1:1")));
+
+ assertFalse(fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("10:10:10:10:10:10:10:10:")));
+ assertFalse(fromFirstAndLast(fromString("1:2:3:4:5:6:7:8"), fromString("9:10:11:12:13:14:15:16"))
+ .contains(fromString("1:1:1:1:1:1:1:1")));
+ }
+
+ @Test
+ public void containsRange()
+ {
+ assertTrue(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))));
+ assertTrue(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(fromFirstAndLast(fromString("::4:4:4:4"), fromString("::5:5:5:5"))));
+ }
+
+ @Test
+ public void doesNotContainRange()
+ {
+ assertFalse(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(fromFirstAndLast(fromString("::1:2:3:3"), fromString("::5:6:7:8"))));
+ assertFalse(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:9"))));
+
+ assertFalse(fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8"))
+ .contains(fromFirstAndLast(fromString("::9:9:9:9"), fromString("::9:9:9:10"))));
+ }
+
+ @Test
+ public void remove()
+ {
+ assertEquals(2, fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::5:5:5:5"))
+ .size());
+ assertEquals(1, fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::1:2:3:4"))
+ .size());
+ assertEquals(1, fromFirstAndLast(fromString("::1:2:3:4"), fromString("::5:6:7:8")).remove(fromString("::8:8:8:8"))
+ .size());
+ assertEquals(0, fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:4")).remove(fromString("::1:2:3:4"))
+ .size());
+ }
+
+ @Test
+ public void iterate()
+ {
+ int amountOfAddresses = 0;
+ for (IPv6Address address : fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:3:8")))
+ {
+ amountOfAddresses++;
+ }
+
+ assertEquals(5, amountOfAddresses);
+ }
+
+ @Test
+ public void compareTo()
+ {
+ final IPv6AddressRange a =
+ fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange b =
+ fromFirstAndLast(fromString("aaaa:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange c =
+ fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("cccc:ffff:ffff:ffff:5:5:5:5"));
+ final IPv6AddressRange d =
+ fromFirstAndLast(fromString("bbbb:ffff:ffff:ffff:1:1:1:1"), fromString("bbbb:ffff:ffff:ffff:5:5:5:5"));
+
+ Assert.assertTrue(a.compareTo(b) > 0);
+ Assert.assertTrue(a.compareTo(c) < 0);
+ Assert.assertTrue(a.compareTo(d) < 0);
+ Assert.assertTrue(b.compareTo(c) < 0);
+ Assert.assertTrue(b.compareTo(d) < 0);
+ Assert.assertTrue(c.compareTo(d) > 0);
+
+ Assert.assertTrue(a.compareTo(a) == 0);
+ Assert.assertTrue(b.compareTo(b) == 0);
+ Assert.assertTrue(c.compareTo(c) == 0);
+ Assert.assertTrue(d.compareTo(d) == 0);
+ }
+
+ @Test
+ public void size()
+ {
+ assertEquals(BigInteger.valueOf(11), fromFirstAndLast(fromString("::"), fromString("::a")).size());
+ assertEquals(BigInteger.valueOf(131074), fromFirstAndLast(fromString("::1:2:3:4"), fromString("::1:2:5:5")).size());
+ assertEquals(BigInteger.valueOf(2).pow(128),
+ fromFirstAndLast(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")).size());
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
new file mode 100644
index 0000000..66798ac
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6AddressTest.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.math.BigInteger;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromInetAddress;
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.*;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6AddressTest
+{
+ @Test
+ public void parseFromAllZeroes()
+ {
+ assertEquals("::", fromString("0000:0000:0000:0000:0000:0000:0000:0000").toString());
+ }
+
+ @Test
+ public void parseFromAllZeroesShortNotation()
+ {
+ assertEquals("::", fromString("::").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddresses()
+ {
+ assertEquals("::1", fromString("0000:0000:0000:0000:0000:0000:0000:0001").toString());
+ assertEquals("::1:0", fromString("0000:0000:0000:0000:0000:0000:0001:0000").toString());
+ assertEquals("1::1:0:0:0", fromString("0001:0000:0000:0000:0001:0000:0000:0000").toString());
+ assertEquals("::ffff", fromString("0000:0000:0000:0000:0000:0000:0000:ffff").toString());
+ assertEquals("ffff::", fromString("ffff:0000:0000:0000:0000:0000:0000:0000").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesShortNotation()
+ {
+ assertEquals("::1", fromString("::1").toString());
+ assertEquals("::1:0", fromString("::1:0").toString());
+ assertEquals("1::1:0:0:0", fromString("1::1:0:0:0").toString());
+ assertEquals("::ffff", fromString("::ffff").toString());
+ assertEquals("ffff::", fromString("ffff::").toString());
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toString());
+ }
+
+ @Test
+ public void parseSomeRealAddressesFromRFC5952()
+ {
+ assertEquals("::", fromString("::").toString());
+ assertEquals("1:2:3:4::", fromString("1:2:3:4::").toString());
+ assertEquals("::1:2:3:4", fromString("::1:2:3:4").toString());
+ assertEquals("1::2", fromString("1::2").toString());
+ assertEquals("::2", fromString("::2").toString());
+ assertEquals("1::", fromString("1::").toString());
+ assertEquals("a31:200:3abc::de4", fromString("0a31:0200:3AbC::0dE4").toString());
+ assertEquals("1::4:0:0:0", fromString("1:0:0:0:4:0:0:0").toString());
+
+ assertEquals("2001:db8::1", fromString("2001:db8::1").toString());
+ assertEquals("2001:db8::2:1", fromString("2001:db8:0:0:0:0:2:1").toString());
+ assertEquals("2001:db8:0:1:1:1:1:1", fromString("2001:db8:0:1:1:1:1:1").toString());
+ assertEquals("2001:db8::1:0:0:1", fromString("2001:db8::1:0:0:1").toString());
+ assertEquals("2001:0:0:1::1", fromString("2001:0:0:1:0:0:0:1").toString());
+
+ assertEquals("1:0:0:4::", fromString("1:0:0:4::").toString());
+ }
+
+ @Test
+ public void parseSomeRealIPv4MappedAddresses()
+ {
+ assertEquals("::ffff:0.0.0.1", fromString("::ffff:0.0.0.1").toString());
+ assertEquals("::ffff:192.168.139.50", fromString("::ffff:192.168.139.50").toString());
+ assertEquals("::ffff:192.168.139.50", fromString("::ffff:c0a8:8b32").toString());
+ }
+
+ @Test
+ public void toLongStringOnSomeRealAddresses()
+ {
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:0001", fromString("::1").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0001:0000", fromString("::1:0").toLongString());
+ assertEquals("0001:0000:0000:0000:0001:0000:0000:0000", fromString("1::1:0:0:0").toLongString());
+ assertEquals("0000:0000:0000:0000:0000:0000:0000:ffff", fromString("::ffff").toLongString());
+ assertEquals("ffff:0000:0000:0000:0000:0000:0000:0000", fromString("ffff::").toLongString());
+ assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", fromString("2001:db8:85a3::8a2e:370:7334").toLongString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_1()
+ {
+ fromString(":");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalid_2()
+ {
+ fromString(":a");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_1()
+ {
+ fromString("a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooShort_2()
+ {
+ fromString("a:a:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void parseInvalidTooLong()
+ {
+ fromString("a:a:a:a:a:a:a:a:a:a:a:a");
+ }
+
+ @Test
+ public void constructFromInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals("2001:db8:85a3::8a2e:370:7334", fromInetAddress(inetAddress).toString());
+ }
+
+ @Test
+ public void convertToInet6Address() throws UnknownHostException
+ {
+ final InetAddress inetAddress = Inet6Address.getByName("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assertEquals(inetAddress, fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").toInetAddress());
+ }
+
+ @Test
+ public void constructFromByteArray() throws UnknownHostException
+ {
+ assertEquals("1:1:1:1:1:1:1:1",
+ IPv6Address.fromByteArray(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01})
+ .toString());
+ }
+
+ @Test
+ public void convertToByteArray() throws UnknownHostException
+ {
+ assertArrayEquals(
+ new byte[]{0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01},
+ fromString("1:1:1:1:1:1:1:1").toByteArray());
+ }
+
+ @Test
+ public void convertToAndFromByteArray()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ byte[] randomBytes = new byte[16];
+ rg.nextBytes(randomBytes);
+
+ final IPv6Address address = IPv6Address.fromByteArray(randomBytes);
+ assertArrayEquals(randomBytes, address.toByteArray());
+ }
+ }
+
+ @Test
+ public void convertToBigInteger() throws UnknownHostException
+ {
+ assertEquals(BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE), IPv6Address.MAX.toBigInteger());
+ assertEquals(BigInteger.ONE.shiftLeft(128).subtract(BigInteger.valueOf(16)),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0").toBigInteger());
+ }
+
+ @Test
+ public void convertToAndFromBigInteger()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ byte[] randomBytes = new byte[16];
+ rg.nextBytes(randomBytes);
+ BigInteger randomBigInteger = new BigInteger(1, randomBytes);
+
+ final IPv6Address address = IPv6Address.fromBigInteger(randomBigInteger);
+ assertEquals(randomBigInteger, address.toBigInteger());
+ }
+ }
+
+ @Test
+ public void positionOfLongestRunOfZeroes()
+ {
+ assertArrayEquals(new int[]{0, 8}, fromString("::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{3, 5}, fromString("a:b:c::").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{2, 5}, fromString("a:b::c").startAndLengthOfLongestRunOfZeroes());
+ assertArrayEquals(new int[]{4, 4}, fromString("a:0:0:c::").startAndLengthOfLongestRunOfZeroes());
+ }
+
+ @Test
+ public void toStringCompactsLongestRunOfZeroes()
+ {
+ assertEquals("0:0:1::", fromString("0:0:1::").toString()); // and not ::1:0:0:0:0:0
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Address address = new IPv6Address(rg.nextLong(), rg.nextLong());
+ assertEquals(address, fromString(address.toString()));
+ }
+ }
+
+ @Test
+ public void addition()
+ {
+ assertEquals(fromString("::2"), fromString("::1").add(1));
+ assertEquals(fromString("::1:0:0:0"), fromString("::ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:0"), fromString("::ffff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::1:0:0:0:1"), fromString("::ffff:ffff:ffff:ffff").add(2));
+ assertEquals(fromString("::8000:0:0:0"), fromString("::7fff:ffff:ffff:ffff").add(1));
+ assertEquals(fromString("::").add(Integer.MAX_VALUE).add(Integer.MAX_VALUE), fromString("::").add(Integer.MAX_VALUE).add(
+ Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void additionOverflow()
+ {
+ assertEquals(fromString("::"), fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").add(1));
+ }
+
+ @Test
+ public void subtraction()
+ {
+ assertEquals(fromString("::1"), fromString("::2").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:ffff"), fromString("::0001:0:0:0:0").subtract(1));
+ assertEquals(fromString("::ffff:ffff:ffff:fffe"), fromString("::0001:0:0:0:0").subtract(2));
+ assertEquals(fromString("::7fff:ffff:ffff:ffff"), fromString("::8000:0:0:0").subtract(1));
+ assertEquals(fromString("::").subtract(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE), fromString("::").subtract(
+ Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void subtractionVersusAdditionWithRandomAddresses()
+ {
+ final Random random = new Random();
+ final int randomInt = random.nextInt();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(randomInt).subtract(randomInt));
+ }
+
+ @Test
+ public void subtractionVersusAdditionCornerCases()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+ assertEquals(randomAddress, randomAddress.add(Integer.MAX_VALUE).subtract(Integer.MAX_VALUE));
+ assertEquals(randomAddress, randomAddress.add(Integer.MIN_VALUE).subtract(Integer.MIN_VALUE));
+ }
+
+ @Test
+ public void subtractionUnderflow()
+ {
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), fromString("::").subtract(1));
+ }
+
+ @Test
+ public void setBit()
+ {
+ assertEquals(fromString("::1"), fromString("::").setBit(0));
+ assertEquals(fromString("::2"), fromString("::").setBit(1));
+ assertEquals(fromString("::3"), fromString("::").setBit(0).setBit(1));
+
+ assertEquals(fromString("0:0:0:1::"), fromString("::").setBit(64));
+ assertEquals(fromString("0:0:0:2::"), fromString("::").setBit(65));
+ }
+
+ @Test
+ public void compare()
+ {
+ assertTrue(0 == fromString("::").compareTo(fromString("::")));
+ assertTrue(0 > fromString("::").compareTo(fromString("::1")));
+ assertTrue(0 < fromString("::1").compareTo(fromString("::")));
+
+ assertTrue(0 > fromString("::").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("::efff:ffff:ffff:ffff").compareTo(fromString("::ffff:ffff:ffff:ffff")));
+ assertTrue(0 > fromString("efff:ffff:ffff:ffff:0:1:2:3").compareTo(fromString("ffff:ffff:ffff:ffff:4:5:6:7")));
+ }
+
+ @Test
+ public void maskWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("2000::"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maskWithNetworkMask(new IPv6NetworkMask(15)));
+ assertEquals(fromString("8000::"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").maskWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void maximumAddressWithPrefixLength()
+ {
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(128)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:0000:8a2e:0370:73ff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7300").maximumAddressWithNetworkMask(new IPv6NetworkMask(120)));
+ assertEquals(fromString("2001:0db8:85a3:0000:ffff:ffff:ffff:ffff"),
+ fromString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").maximumAddressWithNetworkMask(new IPv6NetworkMask(64)));
+ assertEquals(fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("8000::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ assertEquals(fromString("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
+ fromString("7fff::").maximumAddressWithNetworkMask(new IPv6NetworkMask(1)));
+ }
+
+ @Test
+ public void numberOfTrailingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfTrailingOnes());
+ assertEquals(1, fromString("::1").numberOfTrailingOnes());
+ assertEquals(4, fromString("::f").numberOfTrailingOnes());
+
+ final IPv6Address addressWithLowBitsEqualToLongMaxValue = fromString("::7fff:ffff:ffff:ffff");
+ assertEquals(Long.MAX_VALUE, addressWithLowBitsEqualToLongMaxValue.getLowBits());
+ assertEquals(63, addressWithLowBitsEqualToLongMaxValue.numberOfTrailingOnes());
+ }
+
+ @Test
+ public void numberOfLeadingOnes()
+ {
+ assertEquals(0, fromString("::").numberOfLeadingOnes());
+ assertEquals(1, fromString("8000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::").numberOfLeadingOnes());
+ assertEquals(4, fromString("f000::f").numberOfLeadingOnes());
+ assertEquals(65, fromString("ffff:ffff:ffff:ffff:8000::f").numberOfLeadingOnes());
+ }
+
+ @Test
+ public void numberOfTrailingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfTrailingZeroes());
+ assertEquals(127, fromString("8000::").numberOfTrailingZeroes());
+ assertEquals(124, fromString("f000::").numberOfTrailingZeroes());
+ assertEquals(0, fromString("f000::f").numberOfTrailingZeroes());
+ assertEquals(63, fromString("ffff:ffff:ffff:ffff:8000::").numberOfTrailingZeroes());
+ }
+
+ @Test
+ public void numberOfLeadingZeroes()
+ {
+ assertEquals(128, fromString("::").numberOfLeadingZeroes());
+ assertEquals(0, fromString("8000::").numberOfLeadingZeroes());
+ assertEquals(124, fromString("::f").numberOfLeadingZeroes());
+ assertEquals(63, fromString("::1:ffff:ffff:ffff:ffff").numberOfLeadingZeroes());
+ }
+
+ @Test
+ public void isIPv4Mapped()
+ {
+ assertFalse(fromString("::").isIPv4Mapped());
+ assertFalse(fromString("::0001:ffff:1234:5678").isIPv4Mapped());
+ assertFalse(fromString("1::ffff:1234:5678").isIPv4Mapped());
+ assertFalse(fromString("::afff:1234:5678").isIPv4Mapped());
+
+ assertTrue(fromString("::ffff:1234:5678").isIPv4Mapped());
+ assertTrue(fromString("::ffff:192.168.123.123").isIPv4Mapped());
+ }
+
+ @Test
+ public void isMulticast()
+ {
+ assertFalse(fromString("::").isMulticast());
+
+ assertTrue(fromString("ff12::ffff:1234:5678").isMulticast());
+ }
+
+ @Test
+ public void isLinkLocal()
+ {
+ assertFalse(fromString("::").isLinkLocal());
+
+ assertTrue(fromString("fe80::ffff:1234:5678").isLinkLocal());
+ }
+
+ @Test
+ public void isSiteLocal()
+ {
+ assertFalse(fromString("::").isSiteLocal());
+
+ assertTrue(fromString("fec0::ffff:1234:5678").isSiteLocal());
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
new file mode 100644
index 0000000..960d630
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkHelpersTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkHelpersTest
+{
+ @Test
+ public void longestPrefixLength()
+ {
+ assertEquals(128, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("::1")));
+ assertEquals(127, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::")));
+ assertEquals(126, IPv6NetworkHelpers.longestPrefixLength(fromString("::1"), fromString("::2")));
+
+ assertEquals(0, IPv6NetworkHelpers.longestPrefixLength(fromString("::"), fromString("ffff::")));
+ assertEquals(32, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::"), fromString("ffff:ffff:8000::")));
+ assertEquals(65, IPv6NetworkHelpers.longestPrefixLength(fromString("ffff:ffff::8000:2:3:4"), fromString("ffff:ffff::C000:2:3:4")));
+ }
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
new file mode 100644
index 0000000..0eea592
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkMaskTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkMaskTest
+{
+ @Test
+ public void constructValidNetworkMasks()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)), new IPv6NetworkMask(128));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)), new IPv6NetworkMask(127));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffcL)), new IPv6NetworkMask(126));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)), new IPv6NetworkMask(65));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)), new IPv6NetworkMask(64));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xc000000000000000L, 0x0L)), new IPv6NetworkMask(2));
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)), new IPv6NetworkMask(1));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_Negative()
+ {
+ new IPv6NetworkMask(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromPrefixLength_TooBig()
+ {
+ new IPv6NetworkMask(129);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void constructInvalidFromAddress()
+ {
+ IPv6NetworkMask.fromAddress(new IPv6Address(123L, 456L));
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
new file mode 100644
index 0000000..21d9e61
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/IPv6NetworkTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6;
+
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.Random;
+
+import static com.googlecode.ipv6.IPv6Address.fromString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jan Van Besien
+ */
+public class IPv6NetworkTest
+{
+ @Test
+ public void constructFromTwoAddresses()
+ {
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)),
+ IPv6Network.fromTwoAddresses(fromString("::1"), fromString("::2")));
+ assertEquals(IPv6Network.fromAddressAndMask(fromString("a:b::"), IPv6NetworkMask.fromPrefixLength(44)),
+ IPv6Network.fromTwoAddresses(fromString("a:b:c::1:1"), fromString("a:b::f:f")));
+ }
+
+ @Test
+ public void stringRepresentation()
+ {
+ assertEquals("::/126", IPv6Network.fromAddressAndMask(fromString("::"), IPv6NetworkMask.fromPrefixLength(126)).toString());
+ assertEquals("a:b:c:d::/64", IPv6Network.fromAddressAndMask(fromString("a:b:c:d::"), IPv6NetworkMask.fromPrefixLength(64))
+ .toString());
+ }
+
+ @Test
+ public void toStringCanBeUsedInFromStringAndViceVersa()
+ {
+ final int nTests = 10000;
+ final Random rg = new Random();
+
+ for (int i = 0; i < nTests; i++)
+ {
+ final IPv6Network network = IPv6Network.fromAddressAndMask(new IPv6Address(rg.nextLong(), rg.nextLong()),
+ IPv6NetworkMask.fromPrefixLength(rg.nextInt(128) + 1));
+ assertEquals(network, IPv6Network.fromString(network.toString()));
+ }
+ }
+
+ @Test
+ public void constructAndVerifyPrefixLength()
+ {
+ assertEquals(1, IPv6Network.fromString("a:b:c::/1").getNetmask().asPrefixLength());
+ assertEquals(63, IPv6Network.fromString("a:b:c::/63").getNetmask().asPrefixLength());
+ assertEquals(64, IPv6Network.fromString("a:b:c::/64").getNetmask().asPrefixLength());
+ assertEquals(65, IPv6Network.fromString("a:b:c::/65").getNetmask().asPrefixLength());
+ assertEquals(127, IPv6Network.fromString("a:b:c::/127").getNetmask().asPrefixLength());
+ assertEquals(128, IPv6Network.fromString("a:b:c::/128").getNetmask().asPrefixLength());
+ }
+
+ @Test
+ public void constructAndVerifyNetmask()
+ {
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0x8000000000000000L, 0x0L)),
+ IPv6Network.fromString("a:b:c::/1").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xfffffffffffffffeL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/63").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x0L)),
+ IPv6Network.fromString("a:b:c::/64").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0x8000000000000000L)),
+ IPv6Network.fromString("a:b:c::/65").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xfffffffffffffffeL)),
+ IPv6Network.fromString("a:b:c::/127").getNetmask());
+
+ assertEquals(IPv6NetworkMask.fromAddress(new IPv6Address(0xffffffffffffffffL, 0xffffffffffffffffL)),
+ IPv6Network.fromString("a:b:c::/128").getNetmask());
+ }
+
+ @Test
+ public void contains()
+ {
+ assertTrue(IPv6Network.fromString("ffff::/8").contains(IPv6Address.fromString("ffff::1")));
+ assertTrue(IPv6Network.fromString("1234:5678:1234:5678::/64").contains(IPv6Address.fromString("1234:5678:1234:5678:1::")));
+ }
+
+ @Test
+ public void zeroNetworkContainsEverything()
+ {
+ final Random random = new Random();
+ final IPv6Address randomAddress = new IPv6Address(random.nextLong(), random.nextLong());
+
+ assertTrue(IPv6Network.fromString("::/0").contains(randomAddress));
+ assertTrue(IPv6Network.fromString("abcd:effe:dcba::/0").contains(randomAddress));
+ }
+
+ @Test
+ public void iteratorShouldStartWithFirstAndEndWithLast()
+ {
+ IPv6Network ipv6Network = IPv6Network.fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000/126");
+ Iterator iterator = ipv6Network.iterator();
+ int i = 0;
+ for (; iterator.hasNext(); i++)
+ {
+ assertEquals(IPv6Address.fromString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:000" + i), iterator.next());
+ }
+ assertEquals(4, i);
+ }
+
+ @Test
+ public void split()
+ {
+ {
+ IPv6Network slash120 = IPv6Network.fromString("::ffff:192.168.123.0/120");
+ Iterator splits = slash120.split(IPv6NetworkMask.fromPrefixLength(121));
+ verifySplits(splits, 2, IPv6Network.fromString("::ffff:192.168.123.0/121"),
+ IPv6Network.fromString("::ffff:192.168.123.128/121"));
+ }
+
+ {
+ IPv6Network slash30 = IPv6Network.fromString("a:b:c:d:1:2:3:4/30"); // a:8:: is the host address after masking with /30
+ Iterator splits = slash30.split(IPv6NetworkMask.fromPrefixLength(40));
+ verifySplits(splits, (int) Math.pow(2, 40 - 30),
+ IPv6Network.fromString("a:8::/40"),
+ IPv6Network.fromString("a:8:100::/40"),
+ IPv6Network.fromString("a:8:200::/40"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void splitInLargerSize()
+ {
+ IPv6Network ipv6Network = IPv6Network.fromString("1:2:3:4:5:6:7:8/69");
+ ipv6Network.split(IPv6NetworkMask.fromPrefixLength(68)); // 68 subnet is bigger than 69
+ }
+
+ @Test
+ public void splitInSameSize()
+ {
+ IPv6Network ipv6Network = IPv6Network.fromString("1:2:3:4:5:6:7:8/69");
+ Iterator splits = ipv6Network.split(IPv6NetworkMask.fromPrefixLength(69));
+ verifySplits(splits, 1, ipv6Network);
+ }
+
+ /**
+ * Verify a splitted network.
+ *
+ * @param splits splits to verify
+ * @param expectedNbr number of expected splits
+ * @param expectedSplits the first splits in the list to expect (check as many as you want but no need to check them all)
+ */
+ private void verifySplits(Iterator splits, int expectedNbr, IPv6Network... expectedSplits)
+ {
+ int nChecked = 0;
+
+ // check the ones that are explicitely passed
+ for (IPv6Network expectedSplit : expectedSplits)
+ {
+ assertEquals(expectedSplit, splits.next());
+ nChecked++;
+ }
+
+ // merely check count for the others
+ while (splits.hasNext())
+ {
+ splits.next();
+ nChecked++;
+ }
+
+ assertEquals(expectedNbr, nChecked);
+ }
+
+}
diff --git a/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/examples/Examples.java b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/examples/Examples.java
new file mode 100644
index 0000000..dbcee27
--- /dev/null
+++ b/tags/java-ipv6-0.14/src/test/java/com/googlecode/ipv6/examples/Examples.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2013 Jan Van Besien
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.ipv6.examples;
+
+import com.googlecode.ipv6.*;
+import org.junit.Test;
+
+import java.util.Iterator;
+
+/**
+ * Some examples also featured in the online documentation. This class is in a separate package on purpose, such that we make sure only to
+ * call methods of the public API.
+ *
+ * @author Jan Van Besien
+ */
+public class Examples
+{
+ @Test
+ public void ipAddressConstruction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address iPv4MappedIPv6Address = IPv6Address.fromString("::ffff:192.168.0.1");
+ }
+
+ @Test
+ public void ipAddressAdditionAndSubtraction()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+ final IPv6Address next = iPv6Address.add(1);
+ final IPv6Address previous = iPv6Address.subtract(1);
+ System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
+ System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e
+ }
+
+ @Test
+ public void ipAddressRangeConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
+ }
+
+ @Test
+ public void ipNetworkConstruction()
+ {
+ final IPv6AddressRange range = IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
+ final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
+ System.out.println(range.equals(network)); // prints true
+ }
+
+ @Test
+ public void ipNetworkCalculation()
+ {
+ final IPv6Network strangeNetwork = IPv6Network.fromString("fe80::226:2dff:fefa:cd1f/43");
+
+ System.out.println(strangeNetwork.getFirst()); // prints fe80::
+ System.out.println(strangeNetwork.getLast()); // prints fe80:0:1f:ffff:ffff:ffff:ffff:ffff
+ System.out.println(strangeNetwork.getNetmask().asPrefixLength()); // prints 43
+ System.out.println(strangeNetwork.getNetmask().asAddress()); // prints ffff:ffff:ffe0::
+ }
+
+ @Test
+ public void ipNetworkSubnetCalculation()
+ {
+ final IPv6Network network = IPv6Network.fromString("1:2:3:4:5:6:7:0/120");
+
+ Iterator splits = network.split(IPv6NetworkMask.fromPrefixLength(124));
+ while (splits.hasNext())
+ System.out.println(splits.next());
+
+ // prints 1:2:3:4:5:6:7:0/124, 1:2:3:4:5:6:7:10/124, 1:2:3:4:5:6:7:20/124, ... until 1:2:3:4:5:6:7:f0/124 (16 in total)
+ }
+
+ @Test
+ public void ipNetworkNotationChoices()
+ {
+ IPv6Network prefixLengthNotation = IPv6Network.fromString("::1/16");
+ IPv6Network addressNotation =
+ IPv6Network.fromAddressAndMask(IPv6Address.fromString("::"), IPv6NetworkMask.fromAddress(IPv6Address.fromString("ffff::")));
+ System.out.println(prefixLengthNotation.equals(addressNotation)); // prints true
+ System.out.println(prefixLengthNotation); // prints ::/16
+ System.out.println(prefixLengthNotation.getFirst() + "/" + prefixLengthNotation.getNetmask().asAddress()); // prints ::/ffff::
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void ipNetworkMaskConstruction()
+ {
+ final IPv6NetworkMask slash40Network = IPv6NetworkMask.fromPrefixLength(40);
+ System.out.println(slash40Network.asAddress()); // prints ffff:ffff:ff00::
+ System.out.println(slash40Network.asPrefixLength()); // prints 40
+
+ final IPv6NetworkMask slash40NetworkConstructedFromAddressNotation = IPv6NetworkMask.fromAddress(
+ IPv6Address.fromString("ffff:ffff:ff00::"));
+ System.out.println(slash40Network.equals(slash40NetworkConstructedFromAddressNotation)); // prints true
+
+ final IPv6NetworkMask invalidNetworkMask = IPv6NetworkMask.fromAddress(IPv6Address.fromString("0fff::")); // fails
+ }
+
+ @Test
+ public void ipAddressNetworkMasking()
+ {
+ final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
+
+ final IPv6Address masked = iPv6Address.maskWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(masked.toString()); // prints fe80::
+
+ final IPv6Address maximum = iPv6Address.maximumAddressWithNetworkMask(IPv6NetworkMask.fromPrefixLength(40));
+ System.out.println(maximum.toString()); // prints fe80:0:ff:ffff:ffff:ffff:ffff:ffff
+ }
+
+ @Test
+ public void poolExample()
+ {
+ final IPv6AddressPool pool = IPv6AddressPool.fromRangeAndSubnet(
+ IPv6AddressRange.fromFirstAndLast(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
+ IPv6Address.fromString("fe80::226:2dff:fefa:ffff")),
+ IPv6NetworkMask.fromPrefixLength(120));
+ System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
+
+ final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
+ System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false
+ }
+
+}
diff --git a/tags/java-ipv6-0.15/LICENSE b/tags/java-ipv6-0.15/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tags/java-ipv6-0.15/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/java-ipv6-0.15/NOTICE b/tags/java-ipv6-0.15/NOTICE
new file mode 100644
index 0000000..a4605c4
--- /dev/null
+++ b/tags/java-ipv6-0.15/NOTICE
@@ -0,0 +1,2 @@
+ Java IPv6
+ Copyright 2013 Jan Van Besien
\ No newline at end of file
diff --git a/tags/java-ipv6-0.15/pom.xml b/tags/java-ipv6-0.15/pom.xml
new file mode 100644
index 0000000..f79be15
--- /dev/null
+++ b/tags/java-ipv6-0.15/pom.xml
@@ -0,0 +1,149 @@
+
+
+
+ 4.0.0
+
+
+