Skip to content

Commit 3142abd

Browse files
committed
Prepare release 0.1.0
1 parent 5470e01 commit 3142abd

16 files changed

Lines changed: 8098 additions & 3133 deletions

File tree

README.md

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,19 @@
2323
— _Sir Evander Marchbank_, self-proclaimed cider cartographer who insists that every orchard has its own “gravitational pull” affecting the bubbles.
2424

2525
---
26+
2627
CIDRE focuses on parsing and representing IP addresses, IP networks and providing CIDR math. On the JVM and Android it maps from/to `InetAddress`/`Inet4Address`/`Inet6Address`. On native targets, it maps from/to `in_addr`/`in6_addr`.
2728
It is not a full IP networking implementation, but you can use it to implement IP routing.
2829
It has a total of zero external dependencies.
29-
Currently, CIDRE can
30-
* parse and encode IPv4 and IPv6 addresses from/to String and ByteArray representations
31-
* iterate over, slice, split, and merge networks,
32-
* convert prefixes from/to netmasks
33-
* subnet and supernet
34-
* sorting of networks and addresses
30+
Currently, CIDRE provides the following functionality:
31+
* parsing and encoding IPv4 and IPv6 addresses from/to String and ByteArray representations
32+
* converting CIDR prefixes from/to netmasks
33+
* checking whether addresses or networks are are fully contained within a network
34+
* comparability of networks and addresses inside a family (IPv4/IPv6)
35+
36+
Planned features are:
37+
* iterating over, slicing, splitting, and merging networks
38+
* subnetting and supernetting
3539

3640

3741
In general, CIDRE's data model has semantics influenced by [netaddr](https://github.com/netaddr/netaddr/?tab=readme-ov-file): An `IpNetwork` covers a range of `IpInterface`s, both of which consist of an `IpAddress` and a `prefix`.
@@ -78,6 +82,10 @@ Simply `toString()` any IP address to get its string representation, or access `
7882
An `IpAddress`'s companion object also provides helpful properties such as segment separator, number of octets, and readily usable `Regex` instances to check whether a string is a valid representation of
7983
an IP address or a single address segment.
8084

85+
#### Ordering
86+
87+
IP addresses are `Comparable` inside a family (IPv4, IPv6) and are ordered by comparing their octets interpreted as a BE-encoded unsigned integer.
88+
8189
#### Platform Interop
8290

8391
CIDRE's `IpAddress` classes conveniently map from/to platform types.
@@ -108,19 +116,69 @@ Still, the flag `isIpv4Compatible` indicates whether an IPv6 address conforms to
108116
It is possible to extract the contained IPv4 address from an IPv4-mapped or IPv4-compatible address by accessing the
109117
`embeddedIpV4Address` property. It returns null if no IPv4 address is contained.
110118

119+
### Working with Networks and IpInterfaces
120+
121+
CIDRE models two closely related concepts:
122+
- `IpNetwork`: a contiguous address range, defined by a network address and prefix.
123+
The network’s address itself is part of the network (and for IPv4, the broadcast address is also considered inside for membership checks).
124+
- `IpInterface`: a single address bound to a prefix and associated with a network, and therefore carry a reference to their associated `IpNetwork`.
125+
126+
Both share the `IpAddressAndPrefix` interface and its respective IPv4 and IPv6 specializations and therefore expose:
127+
- `address` and prefix (CIDR prefix length)
128+
- `netmask` (network-order ByteArray)
129+
- common flags (e.g., `isLinkLocal`, `isLoopback`, `isMulticast`). IPv4- and IPv6-specific flags are available on their
130+
respective interfaces (IpAddressAndPrefix.V4 / V6).
131+
- consistent `toString()` behavior with address/prefix; IPv4 variants also support netmask printing helpers.
132+
133+
134+
#### Creating IpInterfaces from Networks
111135

112-
### Working with Networks IpInterfaces
136+
Given an `IpAddress` and a prefix, it is possible to get the corresponding network in two ways:
137+
- `IpNetwork(address strict = false)` to create a new `IpNetwork` and deep-copy the ip address into the network's `address` property.
138+
- If `strict = true` the passed address must already be the network address (i.e., correctly masked), according to the specified prefix
139+
- If `strict = false` the passed address will be masked to the network address, according to the specified prefix
140+
- `IpNetwork.forAddress(address, prefix)` creates a new network, referencing and masking the passed `address`. This avoids copying, but modifies any not-correctly-masked address in-place, according to the given `prefix`.
113141

114142
#### Netmasks and Prefixes
115143

144+
CIDRE uses type aliases
145+
- Prefix is a UInt (`typealias Prefix = UInt`)
146+
- Netmask is a network-order byte array (`typealias Netmask = ByteArray`)
147+
148+
Round-tripping between prefixes and netmasks is straightforward:
149+
- Create a netmask from a prefix:
150+
- For a specific IP family: `prefix.toNetmask(IpAddress.Family.V4)` or `prefix.toNetmask(IpAddress.Family.V6)`
151+
- For an arbitrary octet count: `prefix.toNetmask(octetCount)`
152+
- Convert a netmask back to a prefix and validate contiguity: `netmask.toPrefix()`
153+
154+
IP addresses can be masked in-place by calling either `mask(prefix)` or `mask(netmask)`.
155+
To create a deep-copied masked version of an address, manually `copy()` it before masking.
156+
157+
For IPv4, it is also possible to get a dotted-quad representation and choose a preferred textual form when working with `IpAddressAndPrefix`:
158+
- `netmaskToString()` yields a `#.#.#.#` string
159+
- `toString(preferNetmaskOverPrefix = true)` prints `A.A.A.A N.N.N.N`, where `A` is an IP address quad and `N` is a netmask quad.
160+
- `toString(preferNetmaskOverPrefix = false)` prints standard `#.#.#.#/prefix`
161+
116162
#### Host Ranges and Address Spaces
117163

118-
#### Containment, Overlap Checks, and Adjacency
164+
Conceptually:
165+
- An `IpNetwork` represents a contiguous range of addresses.
166+
- An `IpInterface` is a single address bound to a prefix.
167+
- The network address is part of the network; for IPv4, the broadcast address (when applicable) is also inside.
119168

120-
To check whether an address, a network, or an `IpInterface` falls inside a target network, call `targetNetwork.contanins(addrNwOrIf)`.
169+
Coming soon:
170+
- Iteration over the full address space of a network
171+
- Convenience accessors for first/last interface and broadcast (where applicable)
172+
- Efficient network size computations
121173

122174

123-
#### Subnetting and Supernetting
175+
#### Containment
176+
177+
Containment checks are explicit (and fast!):
178+
- Address in network: `network.contains(ipAddress)`
179+
- Interface in network: `network.contains(ipInterface)`
180+
- Network fully contained in another network: `anotherNetwork.contains(network)`
181+
124182

125183

126184
### Low-Level Utilities
@@ -129,10 +187,19 @@ The `at.asitplus.cidre.byteops` package provides low-level helper functions:
129187
* `infix fun ByteArray.and(other: ByteArray): ByteArray` performing a logical `AND` operation, returning a fresh ByteArray.
130188
* `fun ByteArray.andInplace(other: ByteArray): Int` performing an in-place logical `AND` operation, modifying the receiver ByteArray. Returns the number of modified bits.
131189
* `fun ByteArray.compareUnsignedBE(other: ByteArray): Int` comparing two same-sized byte arrays by interpreting their contents as unsigned BE integers
132-
* `fun Prefix.toNetmask(version: IpAddress.Version): Netmask` converting an `UInt` CIDR prefix to its byte representation
190+
* `fun Prefix.toNetmask(family: IpAddress.Family): Netmask` converting an `UInt` CIDR prefix to its byte representation
133191
* `fun Netmask.toPrefix(): Prefix` converting a netmask into its CIDR prefix length
134192
* `ByteArray.toShortArray(bigEndian: Boolean = true): ShortArray` grouping pairs of bytes into a short. Useful to get IPv6 hextets from octets.
135193

194+
195+
## Roadmap:
196+
- More comprehensive tests
197+
- Address range enumeration
198+
- Subnet enumeration (absolute and relative, e.g., `/24` or “+2 bits”)
199+
- Supernetting helpers (absolute and relative)
200+
- Overlap and adjacency checks
201+
- Safe aggregation of adjacent/overlapping ranges where possible and merging of networks
202+
136203
## Contributing
137204
External contributions are greatly appreciated!
138205
Just be sure to observe the contribution guidelines (see [CONTRIBUTING.md](CONTRIBUTING.md)).

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
kotlin("multiplatform") version "2.2.10" apply false
55
kotlin("plugin.serialization") version "2.2.10" apply false
66
id("com.android.library") version "8.9.3" apply false
7-
id("org.jetbrains.dokka") version "2.0.0"
7+
id("org.jetbrains.dokka") version "2.1.0-Beta"
88
id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
99
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.17.0"
1010
}

cidre/api/android/cidre.api

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,36 @@ public abstract class at/asitplus/cidre/IpAddress : java/lang/Comparable {
1212
public synthetic fun <init> ([BLat/asitplus/cidre/IpAddress$Specification;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
1313
public fun compareTo (Lat/asitplus/cidre/IpAddress;)I
1414
public synthetic fun compareTo (Ljava/lang/Object;)I
15+
public final fun copy ()Lat/asitplus/cidre/IpAddress;
1516
public fun equals (Ljava/lang/Object;)Z
17+
public final fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
1618
public final fun getOctets ()[B
1719
public abstract fun getSegments ()Ljava/util/List;
18-
public final fun getVersion ()Lat/asitplus/cidre/IpAddress$Version;
1920
public fun hashCode ()I
2021
public final fun isSpecified ()Z
22+
public final fun mask ([B)I
23+
public final fun mask-WZ4Q5Ns (I)I
2124
}
2225

2326
public final class at/asitplus/cidre/IpAddress$Companion {
2427
public final fun invoke (Ljava/lang/String;)Lat/asitplus/cidre/IpAddress;
2528
public final fun invoke ([B)Lat/asitplus/cidre/IpAddress;
2629
}
2730

31+
public final class at/asitplus/cidre/IpAddress$Family : java/lang/Enum {
32+
public static final field V4 Lat/asitplus/cidre/IpAddress$Family;
33+
public static final field V6 Lat/asitplus/cidre/IpAddress$Family;
34+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
35+
public final fun getNumberOfOctets ()I
36+
public static fun valueOf (Ljava/lang/String;)Lat/asitplus/cidre/IpAddress$Family;
37+
public static fun values ()[Lat/asitplus/cidre/IpAddress$Family;
38+
}
39+
2840
public abstract interface class at/asitplus/cidre/IpAddress$Specification {
41+
public abstract fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
2942
public abstract fun getNumberOfOctets ()I
3043
public abstract fun getRegex ()Lat/asitplus/cidre/IpAddress$Specification$RegexSpec;
3144
public abstract fun getSegmentSeparator ()C
32-
public abstract fun getVersion ()Lat/asitplus/cidre/IpAddress$Version;
3345
}
3446

3547
public abstract class at/asitplus/cidre/IpAddress$Specification$RegexSpec {
@@ -58,10 +70,10 @@ public final class at/asitplus/cidre/IpAddress$V4$Class : java/lang/Enum {
5870
}
5971

6072
public final class at/asitplus/cidre/IpAddress$V4$Companion : at/asitplus/cidre/IpAddress$Specification {
73+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
6174
public fun getNumberOfOctets ()I
6275
public fun getRegex ()Lat/asitplus/cidre/IpAddress$Specification$RegexSpec;
6376
public fun getSegmentSeparator ()C
64-
public fun getVersion ()Lat/asitplus/cidre/IpAddress$Version;
6577
public final fun invoke (Ljava/lang/String;)Lat/asitplus/cidre/IpAddress$V4;
6678
}
6779

@@ -77,33 +89,29 @@ public final class at/asitplus/cidre/IpAddress$V6 : at/asitplus/cidre/IpAddress
7789
}
7890

7991
public final class at/asitplus/cidre/IpAddress$V6$Companion : at/asitplus/cidre/IpAddress$Specification {
92+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
8093
public fun getNumberOfOctets ()I
8194
public final fun getPREFIX_IPV4_COMPAT ()[B
8295
public final fun getPREFIX_IPV4_MAPPED ()[B
8396
public fun getRegex ()Lat/asitplus/cidre/IpAddress$Specification$RegexSpec;
8497
public fun getSegmentSeparator ()C
85-
public fun getVersion ()Lat/asitplus/cidre/IpAddress$Version;
8698
public final fun invoke (Ljava/lang/String;)Lat/asitplus/cidre/IpAddress$V6;
8799
}
88100

89-
public final class at/asitplus/cidre/IpAddress$Version : java/lang/Enum {
90-
public static final field V4 Lat/asitplus/cidre/IpAddress$Version;
91-
public static final field V6 Lat/asitplus/cidre/IpAddress$Version;
92-
public static fun getEntries ()Lkotlin/enums/EnumEntries;
93-
public final fun getNumberOfOctets ()I
94-
public static fun valueOf (Ljava/lang/String;)Lat/asitplus/cidre/IpAddress$Version;
95-
public static fun values ()[Lat/asitplus/cidre/IpAddress$Version;
96-
}
97-
98101
public abstract interface class at/asitplus/cidre/IpAddressAndPrefix {
99102
public abstract fun getAddress ()Lat/asitplus/cidre/IpAddress;
103+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
100104
public abstract fun getNetmask ()[B
101105
public abstract fun getPrefix-pVg5ArA ()I
102106
public abstract fun isLinkLocal ()Z
103107
public abstract fun isLoopback ()Z
104108
public abstract fun isMulticast ()Z
105109
}
106110

111+
public final class at/asitplus/cidre/IpAddressAndPrefix$DefaultImpls {
112+
public static fun getFamily (Lat/asitplus/cidre/IpAddressAndPrefix;)Lat/asitplus/cidre/IpAddress$Family;
113+
}
114+
107115
public abstract interface class at/asitplus/cidre/IpAddressAndPrefix$V4 : at/asitplus/cidre/IpAddressAndPrefix {
108116
public abstract fun isPrivate ()Z
109117
public abstract fun isPublic ()Z
@@ -112,6 +120,7 @@ public abstract interface class at/asitplus/cidre/IpAddressAndPrefix$V4 : at/asi
112120
}
113121

114122
public final class at/asitplus/cidre/IpAddressAndPrefix$V4$DefaultImpls {
123+
public static fun getFamily (Lat/asitplus/cidre/IpAddressAndPrefix$V4;)Lat/asitplus/cidre/IpAddress$Family;
115124
public static fun netmaskToString (Lat/asitplus/cidre/IpAddressAndPrefix$V4;)Ljava/lang/String;
116125
public static fun toString (Lat/asitplus/cidre/IpAddressAndPrefix$V4;Z)Ljava/lang/String;
117126
}
@@ -129,13 +138,15 @@ public abstract interface class at/asitplus/cidre/IpAddressAndPrefix$V6 : at/asi
129138
}
130139

131140
public final class at/asitplus/cidre/IpAddressAndPrefix$V6$DefaultImpls {
141+
public static fun getFamily (Lat/asitplus/cidre/IpAddressAndPrefix$V6;)Lat/asitplus/cidre/IpAddress$Family;
132142
public static fun toString (Lat/asitplus/cidre/IpAddressAndPrefix$V6;Z)Ljava/lang/String;
133143
}
134144

135145
public abstract class at/asitplus/cidre/IpInterface : at/asitplus/cidre/IpAddressAndPrefix {
136146
public static final field Companion Lat/asitplus/cidre/IpInterface$Companion;
137147
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress;ILat/asitplus/cidre/IpNetwork;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
138148
public fun getAddress ()Lat/asitplus/cidre/IpAddress;
149+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
139150
public fun getNetmask ()[B
140151
public final fun getNetwork ()Lat/asitplus/cidre/IpNetwork;
141152
public fun getPrefix-pVg5ArA ()I
@@ -154,6 +165,7 @@ public final class at/asitplus/cidre/IpInterface$V4 : at/asitplus/cidre/IpInterf
154165
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress$V4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
155166
public fun getAddress ()Lat/asitplus/cidre/IpAddress$V4;
156167
public synthetic fun getAddress ()Lat/asitplus/cidre/IpAddress;
168+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
157169
public fun getNetmask ()[B
158170
public fun getPrefix-pVg5ArA ()I
159171
public fun isLinkLocal ()Z
@@ -170,6 +182,7 @@ public final class at/asitplus/cidre/IpInterface$V6 : at/asitplus/cidre/IpInterf
170182
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress$V6;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
171183
public fun getAddress ()Lat/asitplus/cidre/IpAddress$V6;
172184
public synthetic fun getAddress ()Lat/asitplus/cidre/IpAddress;
185+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
173186
public fun getNetmask ()[B
174187
public fun getPrefix-pVg5ArA ()I
175188
public fun isDiscardOnly ()Z
@@ -195,28 +208,16 @@ public abstract class at/asitplus/cidre/IpNetwork : at/asitplus/cidre/IpAddressA
195208
public final fun contains (Lat/asitplus/cidre/IpInterface;)Z
196209
public final fun contains (Lat/asitplus/cidre/IpNetwork;)Z
197210
public fun equals (Ljava/lang/Object;)Z
198-
public final fun first ()Lat/asitplus/cidre/IpInterface;
199211
public fun getAddress ()Lat/asitplus/cidre/IpAddress;
200-
public abstract fun getAddressSpace ()Lkotlin/sequences/Sequence;
201-
public final fun getHostPart ()[B
202-
public final fun getHostRange ()Lkotlin/sequences/Sequence;
212+
public fun getFamily ()Lat/asitplus/cidre/IpAddress$Family;
203213
public fun getNetmask ()[B
204-
public final fun getNetworkPart ()[B
205214
public fun getPrefix-pVg5ArA ()I
206-
public final fun getSize ()J
207215
public final fun getSpecialRanges ()Lat/asitplus/cidre/IpNetwork$SpecialRanges;
208216
public fun hashCode ()I
209217
public final fun interfaceFor (Lat/asitplus/cidre/IpAddress;)Lat/asitplus/cidre/IpInterface;
210-
public final fun isAdjacentTo (Lat/asitplus/cidre/IpNetwork;)Z
211218
public fun isLinkLocal ()Z
212219
public fun isLoopback ()Z
213220
public fun isMulticast ()Z
214-
public final fun isSubnetOf (Lat/asitplus/cidre/IpNetwork;)Z
215-
public final fun last ()Lat/asitplus/cidre/IpInterface;
216-
public final fun overlapsWith (Lat/asitplus/cidre/IpNetwork;)Z
217-
public abstract fun plus (Lat/asitplus/cidre/IpAddress;)Lat/asitplus/cidre/IpAddress;
218-
public final fun subnet-WZ4Q5Ns (I)Lkotlin/sequences/Sequence;
219-
public final fun subnetRelative-WZ4Q5Ns (I)Lkotlin/sequences/Sequence;
220221
public fun toString ()Ljava/lang/String;
221222
}
222223

@@ -242,13 +243,9 @@ public final class at/asitplus/cidre/IpNetwork$V4 : at/asitplus/cidre/IpNetwork,
242243
public static final field Companion Lat/asitplus/cidre/IpNetwork$V4$Companion;
243244
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress$V4;IZILkotlin/jvm/internal/DefaultConstructorMarker;)V
244245
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress$V4;IZLkotlin/jvm/internal/DefaultConstructorMarker;)V
245-
public fun getAddressSpace ()Lkotlin/sequences/Sequence;
246-
public final fun getBroadcastAddress ()Lat/asitplus/cidre/IpInterface;
247246
public fun isPrivate ()Z
248247
public fun isPublic ()Z
249248
public fun netmaskToString ()Ljava/lang/String;
250-
public fun plus (Lat/asitplus/cidre/IpAddress$V4;)Lat/asitplus/cidre/IpAddress$V4;
251-
public synthetic fun plus (Lat/asitplus/cidre/IpAddress;)Lat/asitplus/cidre/IpAddress;
252249
public fun toString (Z)Ljava/lang/String;
253250
}
254251

@@ -274,7 +271,6 @@ public final class at/asitplus/cidre/IpNetwork$V6 : at/asitplus/cidre/IpNetwork,
274271
public static final field Companion Lat/asitplus/cidre/IpNetwork$V6$Companion;
275272
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress$V6;IZILkotlin/jvm/internal/DefaultConstructorMarker;)V
276273
public synthetic fun <init> (Lat/asitplus/cidre/IpAddress$V6;IZLkotlin/jvm/internal/DefaultConstructorMarker;)V
277-
public fun getAddressSpace ()Lkotlin/sequences/Sequence;
278274
public fun isDiscardOnly ()Z
279275
public fun isDocumentation ()Z
280276
public fun isGlobalUnicast ()Z
@@ -283,8 +279,6 @@ public final class at/asitplus/cidre/IpNetwork$V6 : at/asitplus/cidre/IpNetwork,
283279
public fun isReserved ()Z
284280
public fun isUniqueLocal ()Z
285281
public fun isUniqueLocalLocallyAssigned ()Z
286-
public fun plus (Lat/asitplus/cidre/IpAddress$V6;)Lat/asitplus/cidre/IpAddress$V6;
287-
public synthetic fun plus (Lat/asitplus/cidre/IpAddress;)Lat/asitplus/cidre/IpAddress;
288282
public fun toString (Z)Ljava/lang/String;
289283
}
290284

@@ -318,7 +312,7 @@ public final class at/asitplus/cidre/byteops/ByteOpsKt {
318312
public static final fun andInplace ([B[B)I
319313
public static final fun compareUnsignedBE ([B[B)I
320314
public static final fun toNetmask-qim9Vi0 (II)[B
321-
public static final fun toNetmask-qim9Vi0 (ILat/asitplus/cidre/IpAddress$Version;)[B
315+
public static final fun toNetmask-qim9Vi0 (ILat/asitplus/cidre/IpAddress$Family;)[B
322316
public static final fun toPrefix ([B)I
323317
public static final fun toShortArray ([BZ)[S
324318
public static synthetic fun toShortArray$default ([BZILjava/lang/Object;)[S

0 commit comments

Comments
 (0)