cidr-tools 12.1.3 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -11
- package/dist/index.d.ts +12 -6
- package/dist/index.js +126 -87
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -33,24 +33,27 @@ parseCidr("::/64");
|
|
|
33
33
|
|
|
34
34
|
All functions take CIDR addresses or single IP addresses. On single addresses, a prefix of `/32` or `/128` is assumed. Function that return networks will return a merged and sorted set of networks with IPv4 sorted before IPv6.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Networks are validated with [cidr-regex](https://github.com/silverwind/cidr-regex) and anything that is not a CIDR or IP address throws, including out-of-family prefixes like `1.2.3.4/33` and ambiguous zero-padded octets like `010.0.0.1`.
|
|
37
|
+
|
|
38
|
+
Every function takes a trailing `[opts]` *Object*:
|
|
39
|
+
- `validate`: Whether to reject networks that are not a CIDR or IP address. `false` skips the check for input already known to be valid, and then malformed input is parsed on a best-effort basis. Default: `true`.
|
|
37
40
|
|
|
38
41
|
This module requires [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#browser_compatibility) support in your environment.
|
|
39
42
|
|
|
40
|
-
### mergeCidr(networks)
|
|
43
|
+
### mergeCidr(networks, [opts])
|
|
41
44
|
|
|
42
45
|
- `networks` *String* or *Array*: One or more CIDR or IP addresses.
|
|
43
46
|
|
|
44
47
|
Returns an array of merged networks.
|
|
45
48
|
|
|
46
|
-
### excludeCidr(baseNetworks, excludeNetworks)
|
|
49
|
+
### excludeCidr(baseNetworks, excludeNetworks, [opts])
|
|
47
50
|
|
|
48
51
|
- `baseNetworks` *String* or *Array*: One or more CIDR or IP addresses.
|
|
49
52
|
- `excludeNetworks` *String* or *Array*: One or more CIDR or IP addresses to exclude from `baseNetworks`.
|
|
50
53
|
|
|
51
54
|
Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`.
|
|
52
55
|
|
|
53
|
-
### expandCidr(networks)
|
|
56
|
+
### expandCidr(networks, [opts])
|
|
54
57
|
|
|
55
58
|
- `networks` *String* or *Array*: One or more CIDR or IP addresses.
|
|
56
59
|
|
|
@@ -67,14 +70,14 @@ if (end - start >= 1000000n) {
|
|
|
67
70
|
}
|
|
68
71
|
```
|
|
69
72
|
|
|
70
|
-
### overlapCidr(networksA, networksB)
|
|
73
|
+
### overlapCidr(networksA, networksB, [opts])
|
|
71
74
|
|
|
72
75
|
- `networksA` *String* or *Array*: One or more CIDR or IP address.
|
|
73
76
|
- `networksB` *String* or *Array*: One or more CIDR or IP address.
|
|
74
77
|
|
|
75
78
|
Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`.
|
|
76
79
|
|
|
77
|
-
### containsCidr(networksA, networksB)
|
|
80
|
+
### containsCidr(networksA, networksB, [opts])
|
|
78
81
|
|
|
79
82
|
- `networksA` *String* or *Array*: One or more CIDR or IP address.
|
|
80
83
|
- `networksB` *String* or *Array*: One or more CIDR or IP address.
|
|
@@ -87,11 +90,11 @@ Returns a boolean that indicates whether `networksA` fully contain all `networks
|
|
|
87
90
|
|
|
88
91
|
Returns a string or array (depending on input) with a normalized representation. Will not include a prefix on single IPs. Will set network address to the start of the network.
|
|
89
92
|
|
|
90
|
-
`opts`: *Object
|
|
93
|
+
`opts`: *Object*, additionally to `validate`:
|
|
91
94
|
- `compress`: Whether to compress the IP. For IPv6, this means the "best representation" all-lowercase shortest possible form. Default: `true`.
|
|
92
95
|
- `hexify`: Whether to convert IPv4-Mapped IPv6 addresses to hex. Default: `false`.
|
|
93
96
|
|
|
94
|
-
### parseCidr(network)
|
|
97
|
+
### parseCidr(network, [opts])
|
|
95
98
|
|
|
96
99
|
- `network` *String*: A CIDR or IP address.
|
|
97
100
|
|
|
@@ -109,9 +112,7 @@ Returns a `parsed` Object which is used internally by this module. It can be use
|
|
|
109
112
|
## Related
|
|
110
113
|
|
|
111
114
|
- [ip-bigint](https://github.com/silverwind/ip-bigint) - Convert IPv4 and IPv6 addresses to native BigInt and vice-versa
|
|
112
|
-
- [ip-regex](https://github.com/sindresorhus/ip-regex) - Regular expression for matching IP addresses
|
|
113
115
|
- [is-cidr](https://github.com/silverwind/is-cidr) - Check if a string is an IP address in CIDR notation
|
|
114
|
-
- [
|
|
115
|
-
- [cidr-regex](https://github.com/silverwind/cidr-regex) - Check if a string is an IP address in CIDR notation
|
|
116
|
+
- [cidr-regex](https://github.com/silverwind/cidr-regex) - Regular expression for matching IP addresses in CIDR notation and bare IP addresses
|
|
116
117
|
|
|
117
118
|
© [silverwind](https://github.com/silverwind), distributed under BSD licence.
|
package/dist/index.d.ts
CHANGED
|
@@ -11,24 +11,30 @@ type ParsedCidr = {
|
|
|
11
11
|
start: bigint;
|
|
12
12
|
end: bigint;
|
|
13
13
|
};
|
|
14
|
+
type CidrOpts = {
|
|
15
|
+
/** Whether to reject networks that are not a CIDR or IP address. Default: `true` */
|
|
16
|
+
validate?: boolean;
|
|
17
|
+
};
|
|
14
18
|
type NormalizeOpts = {
|
|
19
|
+
/** Whether to reject networks that are not a CIDR or IP address. Default: `true` */
|
|
20
|
+
validate?: boolean;
|
|
15
21
|
compress?: boolean;
|
|
16
22
|
hexify?: boolean;
|
|
17
23
|
};
|
|
18
24
|
/** Returns a string or array (depending on input) with a normalized representation. Will not include a prefix on single IPs. Will set network address to the start of the network. */
|
|
19
25
|
declare function normalizeCidr<T extends Networks>(cidr: T, opts?: NormalizeOpts): T;
|
|
20
26
|
/** Returns a `parsed` Object which is used internally by this module. It can be used to test whether the passed network is IPv4 or IPv6 or to work with the BigInts directly. */
|
|
21
|
-
declare function parseCidr(str: Network): ParsedCidr;
|
|
27
|
+
declare function parseCidr(str: Network, opts?: CidrOpts): ParsedCidr;
|
|
22
28
|
/** Returns an array of merged networks */
|
|
23
|
-
declare function mergeCidr(nets: Networks): Array<Network>;
|
|
29
|
+
declare function mergeCidr(nets: Networks, opts?: CidrOpts): Array<Network>;
|
|
24
30
|
/** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
|
|
25
|
-
declare function excludeCidr(base: Networks, excl: Networks): Array<Network>;
|
|
31
|
+
declare function excludeCidr(base: Networks, excl: Networks, opts?: CidrOpts): Array<Network>;
|
|
26
32
|
/** Returns a generator for individual IPs contained in the networks. */
|
|
27
|
-
declare function expandCidr(nets: Networks): Generator<Network>;
|
|
33
|
+
declare function expandCidr(nets: Networks, opts?: CidrOpts): Generator<Network>;
|
|
28
34
|
/** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
|
|
29
|
-
declare function overlapCidr(a: Networks, b: Networks): boolean;
|
|
35
|
+
declare function overlapCidr(a: Networks, b: Networks, opts?: CidrOpts): boolean;
|
|
30
36
|
/** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
|
|
31
|
-
declare function containsCidr(a: Networks, b: Networks): boolean;
|
|
37
|
+
declare function containsCidr(a: Networks, b: Networks, opts?: CidrOpts): boolean;
|
|
32
38
|
declare const _default: {
|
|
33
39
|
mergeCidr: typeof mergeCidr;
|
|
34
40
|
excludeCidr: typeof excludeCidr;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cidrRegex from "cidr-regex";
|
|
2
|
+
import { parseIp, stringifyIp } from "ip-bigint";
|
|
2
3
|
|
|
3
4
|
//#region index.ts
|
|
4
5
|
const bits = {
|
|
@@ -13,10 +14,30 @@ const hexStrings = Array.from({ length: 256 }, (_, i) => i.toString(16));
|
|
|
13
14
|
const hexPadStrings = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
14
15
|
const hostMasks = Array.from({ length: 129 }, (_, i) => (1n << BigInt(i)) - 1n);
|
|
15
16
|
const hostNotMasks = hostMasks.map((mask) => ~mask);
|
|
17
|
+
const networkRe = cidrRegex({
|
|
18
|
+
exact: true,
|
|
19
|
+
prefix: "optional"
|
|
20
|
+
});
|
|
21
|
+
const unchecked = { validate: false };
|
|
22
|
+
function invalidNetwork(net) {
|
|
23
|
+
return /* @__PURE__ */ new Error(`Network is not a CIDR or IP: "${net}"`);
|
|
24
|
+
}
|
|
25
|
+
function checkNetwork(net) {
|
|
26
|
+
if (!networkRe.test(net)) throw invalidNetwork(net);
|
|
27
|
+
}
|
|
28
|
+
function checkNetworks(nets, opts) {
|
|
29
|
+
if (opts?.validate === false) return;
|
|
30
|
+
if (typeof nets === "string") checkNetwork(nets);
|
|
31
|
+
else for (const net of nets) checkNetwork(net);
|
|
32
|
+
}
|
|
16
33
|
const cmpV4StartEnd = (a, b) => a.start - b.start || a.end - b.end;
|
|
17
34
|
const cmpV4Start = (a, b) => a.start - b.start;
|
|
18
35
|
const cmpV6StartEnd = (a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : a.end > b.end ? 1 : a.end < b.end ? -1 : 0;
|
|
19
36
|
const cmpV6Start = (a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : 0;
|
|
37
|
+
const hostMasks4 = Int32Array.from({ length: 33 }, (_, i) => i === 32 ? -1 : (1 << i) - 1);
|
|
38
|
+
function formatIPv4Fast(n) {
|
|
39
|
+
return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
|
|
40
|
+
}
|
|
20
41
|
function parseIPv4Fast(s, end) {
|
|
21
42
|
let num = 0;
|
|
22
43
|
let octet = 0;
|
|
@@ -24,31 +45,26 @@ function parseIPv4Fast(s, end) {
|
|
|
24
45
|
let digits = 0;
|
|
25
46
|
for (let i = 0; i < end; i++) {
|
|
26
47
|
const c = s.charCodeAt(i);
|
|
27
|
-
if (c
|
|
48
|
+
if (c >= 48 && c <= 57) {
|
|
49
|
+
octet = octet * 10 + (c - 48);
|
|
50
|
+
digits++;
|
|
51
|
+
} else if (c === 46) {
|
|
28
52
|
if (digits === 0 || octet > 255) return -1;
|
|
29
53
|
num = num << 8 | octet;
|
|
30
54
|
octet = 0;
|
|
31
55
|
dots++;
|
|
32
56
|
digits = 0;
|
|
33
|
-
} else if (c >= 48 && c <= 57) {
|
|
34
|
-
octet = octet * 10 + (c - 48);
|
|
35
|
-
digits++;
|
|
36
57
|
} else return -1;
|
|
37
58
|
}
|
|
38
59
|
if (dots !== 3 || digits === 0 || octet > 255) return -1;
|
|
39
60
|
return (num << 8 | octet) >>> 0;
|
|
40
61
|
}
|
|
41
|
-
const hostMasks4 = Int32Array.from({ length: 33 }, (_, i) => i === 32 ? -1 : (1 << i) - 1);
|
|
42
|
-
function formatIPv4Fast(n) {
|
|
43
|
-
return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
|
|
44
|
-
}
|
|
45
62
|
function parsePrefixNum(str, slashIndex) {
|
|
46
|
-
if (slashIndex
|
|
47
|
-
if (slashIndex + 1 >= str.length) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
63
|
+
if (slashIndex + 1 >= str.length) throw invalidNetwork(str);
|
|
48
64
|
let prefixNum = 0;
|
|
49
65
|
for (let i = slashIndex + 1; i < str.length; i++) {
|
|
50
66
|
const c = str.charCodeAt(i);
|
|
51
|
-
if (c < 48 || c > 57) throw
|
|
67
|
+
if (c < 48 || c > 57) throw invalidNetwork(str);
|
|
52
68
|
prefixNum = prefixNum * 10 + (c - 48);
|
|
53
69
|
}
|
|
54
70
|
return prefixNum;
|
|
@@ -58,9 +74,32 @@ let rangeV4End = 0;
|
|
|
58
74
|
let rangeV4Prefix = 0;
|
|
59
75
|
let rangeSlashIndex = -1;
|
|
60
76
|
function parseIPv4Range(str) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
const len = str.length;
|
|
78
|
+
let num = 0;
|
|
79
|
+
let octet = 0;
|
|
80
|
+
let dots = 0;
|
|
81
|
+
let digits = 0;
|
|
82
|
+
let i = 0;
|
|
83
|
+
for (; i < len; i++) {
|
|
84
|
+
const c = str.charCodeAt(i);
|
|
85
|
+
if (c >= 48 && c <= 57) {
|
|
86
|
+
octet = octet * 10 + (c - 48);
|
|
87
|
+
digits++;
|
|
88
|
+
} else if (c === 46) {
|
|
89
|
+
if (digits === 0 || octet > 255) break;
|
|
90
|
+
num = num << 8 | octet;
|
|
91
|
+
octet = 0;
|
|
92
|
+
dots++;
|
|
93
|
+
digits = 0;
|
|
94
|
+
} else break;
|
|
95
|
+
}
|
|
96
|
+
if (i < len && str.charCodeAt(i) === 47) rangeSlashIndex = i;
|
|
97
|
+
else {
|
|
98
|
+
rangeSlashIndex = str.indexOf("/", i);
|
|
99
|
+
if (i < len) return false;
|
|
100
|
+
}
|
|
101
|
+
if (dots !== 3 || digits === 0 || octet > 255) return false;
|
|
102
|
+
const v4num = (num << 8 | octet) >>> 0;
|
|
64
103
|
rangeV4Prefix = rangeSlashIndex !== -1 ? parsePrefixNum(str, rangeSlashIndex) : 32;
|
|
65
104
|
const mask = hostMasks4[Math.max(32 - rangeV4Prefix, 0)];
|
|
66
105
|
rangeV4Start = (v4num & ~mask) >>> 0;
|
|
@@ -75,7 +114,7 @@ function doNormalize(cidr, opts) {
|
|
|
75
114
|
const slashIndex = rangeSlashIndex;
|
|
76
115
|
const prefixPresent = slashIndex !== -1;
|
|
77
116
|
let prefixNum = prefixPresent ? parsePrefixNum(cidr, slashIndex) : -1;
|
|
78
|
-
const { number, version, ipv4mapped, scopeid } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr);
|
|
117
|
+
const { number, version, ipv4mapped, scopeid } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr, unchecked);
|
|
79
118
|
if (prefixNum === -1) prefixNum = bits[version];
|
|
80
119
|
if (version === 4) {
|
|
81
120
|
const hostBits = 32 - prefixNum;
|
|
@@ -83,44 +122,49 @@ function doNormalize(cidr, opts) {
|
|
|
83
122
|
const ip = formatIPv4Fast((Number(number) & ~mask) >>> 0);
|
|
84
123
|
return hostBits > 0 || prefixPresent ? ip + prefixStrings[prefixNum] : ip;
|
|
85
124
|
}
|
|
86
|
-
const
|
|
125
|
+
const compress = opts?.compress ?? true;
|
|
126
|
+
const hexify = opts?.hexify ?? false;
|
|
87
127
|
const hostBits = 128 - prefixNum;
|
|
88
|
-
if (hostBits <= 0 && !prefixPresent) return
|
|
128
|
+
if (hostBits <= 0 && !prefixPresent) return stringifyIp({
|
|
129
|
+
number,
|
|
130
|
+
version,
|
|
131
|
+
ipv4mapped,
|
|
132
|
+
scopeid
|
|
133
|
+
}, {
|
|
89
134
|
compress,
|
|
90
135
|
hexify
|
|
91
136
|
});
|
|
92
137
|
const start = hostBits > 0 ? number & hostNotMasks[hostBits] : number;
|
|
93
|
-
|
|
138
|
+
return stringifyIp({
|
|
94
139
|
number: start,
|
|
95
140
|
version,
|
|
96
141
|
ipv4mapped: ipv4mapped && start >> 32n === 65535n,
|
|
97
142
|
scopeid
|
|
98
|
-
}, {
|
|
99
|
-
return (compress ? ip : normalizeIp(ip, {
|
|
143
|
+
}, {
|
|
100
144
|
compress,
|
|
101
145
|
hexify
|
|
102
|
-
})
|
|
146
|
+
}) + prefixStrings[prefixNum];
|
|
103
147
|
}
|
|
104
148
|
/** Returns a string or array (depending on input) with a normalized representation. Will not include a prefix on single IPs. Will set network address to the start of the network. */
|
|
105
149
|
function normalizeCidr(cidr, opts) {
|
|
150
|
+
checkNetworks(cidr, opts);
|
|
106
151
|
return typeof cidr === "string" ? doNormalize(cidr, opts) : cidr.map((entry) => doNormalize(entry, opts));
|
|
107
152
|
}
|
|
108
153
|
/** Returns a `parsed` Object which is used internally by this module. It can be used to test whether the passed network is IPv4 or IPv6 or to work with the BigInts directly. */
|
|
109
|
-
function parseCidr(str) {
|
|
154
|
+
function parseCidr(str, opts) {
|
|
155
|
+
if (opts?.validate !== false) checkNetwork(str);
|
|
110
156
|
const slashIndex = str.indexOf("/");
|
|
111
|
-
const ipEnd = slashIndex !== -1 ? slashIndex : str.length;
|
|
112
157
|
const prefixPresent = slashIndex !== -1;
|
|
113
|
-
const v4num = parseIPv4Fast(str,
|
|
158
|
+
const v4num = parseIPv4Fast(str, prefixPresent ? slashIndex : str.length);
|
|
114
159
|
if (v4num !== -1) {
|
|
115
160
|
const prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : 32;
|
|
116
161
|
const ip = formatIPv4Fast(v4num);
|
|
117
|
-
const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
|
|
118
162
|
const mask = hostMasks4[Math.max(32 - prefixNum, 0)];
|
|
119
163
|
return {
|
|
120
164
|
cidr: ip + prefixStrings[prefixNum],
|
|
121
165
|
ip,
|
|
122
166
|
version: 4,
|
|
123
|
-
prefix,
|
|
167
|
+
prefix: prefixNumStrings[prefixNum] ?? String(prefixNum),
|
|
124
168
|
prefixPresent,
|
|
125
169
|
start: BigInt((v4num & ~mask) >>> 0),
|
|
126
170
|
end: BigInt((v4num | mask) >>> 0)
|
|
@@ -128,8 +172,7 @@ function parseCidr(str) {
|
|
|
128
172
|
}
|
|
129
173
|
const ipPart = prefixPresent ? str.substring(0, slashIndex) : str;
|
|
130
174
|
let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
|
|
131
|
-
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
|
|
132
|
-
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
175
|
+
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart, unchecked);
|
|
133
176
|
const numBits = bits[version];
|
|
134
177
|
if (prefixNum === -1) prefixNum = numBits;
|
|
135
178
|
const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
|
|
@@ -156,17 +199,11 @@ function parseCidr(str) {
|
|
|
156
199
|
end
|
|
157
200
|
};
|
|
158
201
|
}
|
|
159
|
-
function
|
|
160
|
-
if (parseIPv4Range(str)) return {
|
|
161
|
-
start: rangeV4Start,
|
|
162
|
-
end: rangeV4End,
|
|
163
|
-
version: 4
|
|
164
|
-
};
|
|
202
|
+
function parseCidrLeanSlow(str) {
|
|
165
203
|
const slashIndex = rangeSlashIndex;
|
|
166
204
|
const ipPart = slashIndex !== -1 ? str.substring(0, slashIndex) : str;
|
|
167
205
|
let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
|
|
168
|
-
const { number, version } = parseIp(ipPart);
|
|
169
|
-
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
206
|
+
const { number, version } = parseIp(ipPart, unchecked);
|
|
170
207
|
const numBits = bits[version];
|
|
171
208
|
if (prefixNum === -1) prefixNum = numBits;
|
|
172
209
|
const hostBits = numBits - prefixNum;
|
|
@@ -190,6 +227,14 @@ function parseCidrLean(str) {
|
|
|
190
227
|
version: 6
|
|
191
228
|
};
|
|
192
229
|
}
|
|
230
|
+
function parseCidrLean(str) {
|
|
231
|
+
if (parseIPv4Range(str)) return {
|
|
232
|
+
start: rangeV4Start,
|
|
233
|
+
end: rangeV4End,
|
|
234
|
+
version: 4
|
|
235
|
+
};
|
|
236
|
+
return parseCidrLeanSlow(str);
|
|
237
|
+
}
|
|
193
238
|
function bigintBitLength(n) {
|
|
194
239
|
if (n === 0n) return 0;
|
|
195
240
|
let len = 0;
|
|
@@ -203,10 +248,6 @@ function bigintBitLength(n) {
|
|
|
203
248
|
}
|
|
204
249
|
return len + 32 - Math.clz32(Number(n));
|
|
205
250
|
}
|
|
206
|
-
function biggestPowerOfTwo(num) {
|
|
207
|
-
if (num === 0n) return 0n;
|
|
208
|
-
return 1n << BigInt(bigintBitLength(num) - 1);
|
|
209
|
-
}
|
|
210
251
|
function biggestPowerOfTwo4(num) {
|
|
211
252
|
if (num === 0) return 0;
|
|
212
253
|
if (num >= 4294967296) return 4294967296;
|
|
@@ -236,63 +277,50 @@ function subparts6(pStart, pEnd, output) {
|
|
|
236
277
|
while (start <= pEnd) {
|
|
237
278
|
const size = pEnd - start + 1n;
|
|
238
279
|
const lowBit = start & -start;
|
|
239
|
-
const
|
|
280
|
+
const alignedFully = lowBit !== 0n && lowBit <= size;
|
|
281
|
+
const blockBits = bigintBitLength(alignedFully ? lowBit : size);
|
|
240
282
|
output.push(stringifyIp({
|
|
241
283
|
number: start,
|
|
242
284
|
version: 6
|
|
243
|
-
}) + prefixStrings[129 -
|
|
244
|
-
start +=
|
|
285
|
+
}) + prefixStrings[129 - blockBits]);
|
|
286
|
+
start += alignedFully ? lowBit : 1n << BigInt(blockBits - 1);
|
|
245
287
|
}
|
|
246
288
|
}
|
|
247
289
|
function mergeIntervalsRaw4(nets) {
|
|
248
|
-
if (nets.length
|
|
290
|
+
if (nets.length < 2) return nets;
|
|
249
291
|
nets.sort(cmpV4StartEnd);
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
let curEnd = nets[0].end;
|
|
292
|
+
let last = 0;
|
|
293
|
+
let cur = nets[0];
|
|
253
294
|
for (let i = 1; i < nets.length; i++) {
|
|
254
295
|
const { start, end } = nets[i];
|
|
255
|
-
if (start <=
|
|
256
|
-
if (end >
|
|
296
|
+
if (start <= cur.end + 1) {
|
|
297
|
+
if (end > cur.end) cur.end = end;
|
|
257
298
|
} else {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
});
|
|
262
|
-
curStart = start;
|
|
263
|
-
curEnd = end;
|
|
299
|
+
cur = nets[++last];
|
|
300
|
+
cur.start = start;
|
|
301
|
+
cur.end = end;
|
|
264
302
|
}
|
|
265
303
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
end: curEnd
|
|
269
|
-
});
|
|
270
|
-
return merged;
|
|
304
|
+
nets.length = last + 1;
|
|
305
|
+
return nets;
|
|
271
306
|
}
|
|
272
307
|
function mergeIntervalsRaw6(nets) {
|
|
273
|
-
if (nets.length
|
|
308
|
+
if (nets.length < 2) return nets;
|
|
274
309
|
nets.sort(cmpV6StartEnd);
|
|
275
|
-
|
|
276
|
-
let
|
|
277
|
-
let curEnd = nets[0].end;
|
|
310
|
+
let last = 0;
|
|
311
|
+
let cur = nets[0];
|
|
278
312
|
for (let i = 1; i < nets.length; i++) {
|
|
279
313
|
const { start, end } = nets[i];
|
|
280
|
-
if (start <=
|
|
281
|
-
if (end >
|
|
314
|
+
if (start <= cur.end + 1n) {
|
|
315
|
+
if (end > cur.end) cur.end = end;
|
|
282
316
|
} else {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
});
|
|
287
|
-
curStart = start;
|
|
288
|
-
curEnd = end;
|
|
317
|
+
cur = nets[++last];
|
|
318
|
+
cur.start = start;
|
|
319
|
+
cur.end = end;
|
|
289
320
|
}
|
|
290
321
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
end: curEnd
|
|
294
|
-
});
|
|
295
|
-
return merged;
|
|
322
|
+
nets.length = last + 1;
|
|
323
|
+
return nets;
|
|
296
324
|
}
|
|
297
325
|
function subtractSorted4(bases, excls) {
|
|
298
326
|
if (excls.length === 0) return bases;
|
|
@@ -345,7 +373,8 @@ function subtractSorted6(bases, excls) {
|
|
|
345
373
|
return result;
|
|
346
374
|
}
|
|
347
375
|
/** Returns an array of merged networks */
|
|
348
|
-
function mergeCidr(nets) {
|
|
376
|
+
function mergeCidr(nets, opts) {
|
|
377
|
+
checkNetworks(nets, opts);
|
|
349
378
|
const v4 = [], v6 = [];
|
|
350
379
|
for (const str of typeof nets === "string" ? [nets] : nets) {
|
|
351
380
|
const net = parseCidrLean(str);
|
|
@@ -358,7 +387,9 @@ function mergeCidr(nets) {
|
|
|
358
387
|
return merged;
|
|
359
388
|
}
|
|
360
389
|
/** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
|
|
361
|
-
function excludeCidr(base, excl) {
|
|
390
|
+
function excludeCidr(base, excl, opts) {
|
|
391
|
+
checkNetworks(base, opts);
|
|
392
|
+
checkNetworks(excl, opts);
|
|
362
393
|
const v4base = [], v6base = [];
|
|
363
394
|
const v4excl = [], v6excl = [];
|
|
364
395
|
for (const str of typeof base === "string" ? [base] : base) {
|
|
@@ -377,7 +408,11 @@ function excludeCidr(base, excl) {
|
|
|
377
408
|
return result;
|
|
378
409
|
}
|
|
379
410
|
/** Returns a generator for individual IPs contained in the networks. */
|
|
380
|
-
function
|
|
411
|
+
function expandCidr(nets, opts) {
|
|
412
|
+
checkNetworks(nets, opts);
|
|
413
|
+
return expandChecked(nets);
|
|
414
|
+
}
|
|
415
|
+
function* expandChecked(nets) {
|
|
381
416
|
const v4 = [], v6 = [];
|
|
382
417
|
for (const str of typeof nets === "string" ? [nets] : nets) {
|
|
383
418
|
const net = parseCidrLean(str);
|
|
@@ -424,16 +459,18 @@ function* expandCidr(nets) {
|
|
|
424
459
|
}
|
|
425
460
|
}
|
|
426
461
|
/** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
|
|
427
|
-
function overlapCidr(a, b) {
|
|
462
|
+
function overlapCidr(a, b, opts) {
|
|
463
|
+
checkNetworks(a, opts);
|
|
464
|
+
checkNetworks(b, opts);
|
|
428
465
|
if (typeof a === "string" && typeof b === "string") {
|
|
429
466
|
if (parseIPv4Range(a)) {
|
|
430
467
|
const startA = rangeV4Start, endA = rangeV4End;
|
|
431
468
|
if (parseIPv4Range(b)) return startA <= rangeV4End && rangeV4Start <= endA;
|
|
432
|
-
const pb =
|
|
469
|
+
const pb = parseCidrLeanSlow(b);
|
|
433
470
|
if (pb.version !== 4) return false;
|
|
434
471
|
return startA <= pb.end && pb.start <= endA;
|
|
435
472
|
}
|
|
436
|
-
const pa =
|
|
473
|
+
const pa = parseCidrLeanSlow(a);
|
|
437
474
|
const pb = parseCidrLean(b);
|
|
438
475
|
if (pa.version !== pb.version) return false;
|
|
439
476
|
return pa.start <= pb.end && pb.start <= pa.end;
|
|
@@ -481,16 +518,18 @@ function overlapCidr(a, b) {
|
|
|
481
518
|
return false;
|
|
482
519
|
}
|
|
483
520
|
/** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
|
|
484
|
-
function containsCidr(a, b) {
|
|
521
|
+
function containsCidr(a, b, opts) {
|
|
522
|
+
checkNetworks(a, opts);
|
|
523
|
+
checkNetworks(b, opts);
|
|
485
524
|
if (typeof a === "string" && typeof b === "string") {
|
|
486
525
|
if (parseIPv4Range(a)) {
|
|
487
526
|
const startA = rangeV4Start, endA = rangeV4End;
|
|
488
527
|
if (parseIPv4Range(b)) return startA <= rangeV4Start && endA >= rangeV4End;
|
|
489
|
-
const pb =
|
|
528
|
+
const pb = parseCidrLeanSlow(b);
|
|
490
529
|
if (pb.version !== 4) return false;
|
|
491
530
|
return startA <= pb.start && endA >= pb.end;
|
|
492
531
|
}
|
|
493
|
-
const pa =
|
|
532
|
+
const pa = parseCidrLeanSlow(a);
|
|
494
533
|
const pb = parseCidrLean(b);
|
|
495
534
|
if (pa.version !== pb.version) return false;
|
|
496
535
|
return pa.start <= pb.start && pa.end >= pb.end;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cidr-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"author": "silverwind <me@silverwind.io>",
|
|
5
5
|
"description": "Tools to work with IPv4 and IPv6 CIDR",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"bun": "*"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"
|
|
32
|
+
"cidr-regex": "^7.1.0",
|
|
33
|
+
"ip-bigint": "^10.0.1"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/node": "26.1.2",
|