cidr-tools 9.1.1 → 10.0.1

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.
@@ -0,0 +1,37 @@
1
+ type IPv4Address = string;
2
+ type IPv6Address = string;
3
+ type IPv4CIDR = string;
4
+ type IPv6CIDR = string;
5
+ type Network = IPv4Address | IPv4CIDR | IPv6Address | IPv6CIDR;
6
+ type Networks = Network | Network[];
7
+ type ValidIpVersion = 4 | 6;
8
+ type ParsedCidr = {
9
+ cidr: string;
10
+ ip: string;
11
+ version: ValidIpVersion;
12
+ prefix: string;
13
+ start: bigint;
14
+ end: bigint;
15
+ };
16
+ type NormalizeOpts = {
17
+ compress?: boolean;
18
+ hexify?: boolean;
19
+ };
20
+ export declare function normalizeCidr(cidr: Network, opts?: NormalizeOpts): Network;
21
+ export declare function normalizeCidr(cidr: Network[], opts?: NormalizeOpts): Network[];
22
+ export declare function parseCidr(str: Network): ParsedCidr;
23
+ export declare function mergeCidr(nets: Networks): Network[];
24
+ export declare function excludeCidr(base: Networks, excl: Networks): string[];
25
+ export declare function expandCidr(nets: Networks): string[];
26
+ export declare function overlapCidr(a: Networks, b: Networks): boolean;
27
+ export declare function containsCidr(a: Networks, b: Networks): boolean;
28
+ declare const _default: {
29
+ mergeCidr: typeof mergeCidr;
30
+ excludeCidr: typeof excludeCidr;
31
+ expandCidr: typeof expandCidr;
32
+ overlapCidr: typeof overlapCidr;
33
+ containsCidr: typeof containsCidr;
34
+ normalizeCidr: typeof normalizeCidr;
35
+ parseCidr: typeof parseCidr;
36
+ };
37
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,317 @@
1
+ import { ipVersion, parseIp, stringifyIp, normalizeIp } from 'ip-bigint';
2
+
3
+ const bits = { 4: 32, 6: 128 };
4
+ function uniq(arr) {
5
+ return Array.from(new Set(arr));
6
+ }
7
+ function cidrVersion(cidr) {
8
+ return cidr.includes("/") ? ipVersion(cidr) : 0;
9
+ }
10
+ function compare(a, b) {
11
+ const { number: aNum, version: aVersion } = parseIp(a.replace(/\/.+/, ""));
12
+ const { number: bNum, version: bVersion } = parseIp(b.replace(/\/.+/, ""));
13
+ if (aVersion === bVersion) {
14
+ return aNum - bNum > 0n ? 1 : aNum - bNum < 0n ? -1 : 0;
15
+ } else {
16
+ return aVersion > bVersion ? 1 : 0;
17
+ }
18
+ }
19
+ function doNormalize(cidr, { compress = true, hexify = false } = {}) {
20
+ const { start, end, prefix, version } = parseCidr(cidr);
21
+ if (start !== end) {
22
+ const ip = normalizeIp(stringifyIp({ number: start, version }), { compress, hexify });
23
+ return `${ip}/${prefix}`;
24
+ } else {
25
+ return normalizeIp(cidr, { compress, hexify });
26
+ }
27
+ }
28
+ function normalizeCidr(cidr, opts) {
29
+ if (Array.isArray(cidr)) {
30
+ return cidr.map((entry) => normalizeCidr(entry, opts));
31
+ } else {
32
+ return doNormalize(cidr, opts);
33
+ }
34
+ }
35
+ function parseCidr(str) {
36
+ const cidrVer = cidrVersion(str);
37
+ const parsed = /* @__PURE__ */ Object.create(null);
38
+ let cidr;
39
+ if (cidrVer) {
40
+ cidr = str;
41
+ parsed.version = cidrVer;
42
+ } else {
43
+ const version2 = ipVersion(str);
44
+ if (version2) {
45
+ cidr = `${str}/${bits[version2]}`;
46
+ parsed.version = version2;
47
+ } else {
48
+ throw new Error(`Network is not a CIDR or IP: ${str}`);
49
+ }
50
+ }
51
+ const [ipAndMisc, prefix] = cidr.split("/");
52
+ if (!/^[0-9]+$/.test(prefix)) {
53
+ throw new Error(`Network is not a CIDR or IP: ${str}`);
54
+ }
55
+ const { number, version, ipv4mapped, scopeid } = parseIp(ipAndMisc);
56
+ parsed.ip = stringifyIp({ number, version, ipv4mapped, scopeid });
57
+ parsed.cidr = `${parsed.ip}/${prefix}`;
58
+ parsed.prefix = prefix;
59
+ const numBits = bits[version];
60
+ const ipBits = number.toString(2).padStart(numBits, "0");
61
+ const prefixLen = Number(numBits - Number(prefix));
62
+ const startBits = ipBits.substring(0, numBits - prefixLen);
63
+ parsed.start = BigInt(`0b${startBits}${"0".repeat(prefixLen)}`);
64
+ parsed.end = BigInt(`0b${startBits}${"1".repeat(prefixLen)}`);
65
+ return parsed;
66
+ }
67
+ function doNetsOverlap(a, b) {
68
+ if (a.start > b.end)
69
+ return false;
70
+ if (b.start > a.end)
71
+ return false;
72
+ return true;
73
+ }
74
+ function netContains(a, b) {
75
+ if (b.start < a.start)
76
+ return false;
77
+ if (b.end > a.end)
78
+ return false;
79
+ return true;
80
+ }
81
+ function excludeNets(a, b, v) {
82
+ const parts = [];
83
+ if (a.start > b.end || a.end < b.start) {
84
+ return [a.cidr];
85
+ }
86
+ if (a.start === b.start && a.end === b.end) {
87
+ return [];
88
+ }
89
+ if (a.start > b.start && a.end < b.end) {
90
+ return [];
91
+ }
92
+ if (a.start < b.start && a.end <= b.end) {
93
+ parts.push({ start: a.start, end: b.start - 1n });
94
+ }
95
+ if (a.start >= b.start && a.end > b.end) {
96
+ parts.push({ start: b.end + 1n, end: a.end });
97
+ }
98
+ if (a.start < b.start && a.end > b.end) {
99
+ parts.push(
100
+ { start: a.start, end: b.start - 1n },
101
+ { start: b.end + 1n, end: a.end }
102
+ );
103
+ }
104
+ const remaining = [];
105
+ for (const part of parts) {
106
+ for (const subpart of subparts(part)) {
107
+ remaining.push(formatPart(subpart, v));
108
+ }
109
+ }
110
+ return mergeCidr(remaining);
111
+ }
112
+ function biggestPowerOfTwo(num) {
113
+ if (num === 0n)
114
+ return 0n;
115
+ return 2n ** BigInt(String(num.toString(2).length - 1));
116
+ }
117
+ function subparts(part) {
118
+ if (part.end - part.start === 1n) {
119
+ if (part.end % 2n === 0n) {
120
+ return [{ start: part.start, end: part.start }, { start: part.end, end: part.end }];
121
+ } else {
122
+ return [{ start: part.start, end: part.end }];
123
+ }
124
+ }
125
+ const size = diff(part.end, part.start);
126
+ let biggest = biggestPowerOfTwo(size);
127
+ let start;
128
+ let end;
129
+ if (size === biggest && part.start + size === part.end) {
130
+ return [part];
131
+ } else if (part.start % biggest === 0n) {
132
+ start = part.start;
133
+ end = start + biggest - 1n;
134
+ } else {
135
+ start = part.end / biggest * biggest;
136
+ if (start + biggest - 1n > part.end) {
137
+ start = (part.end / biggest - 1n) * biggest;
138
+ while (start < part.start) {
139
+ biggest /= 2n;
140
+ start = (part.end / biggest - 1n) * biggest;
141
+ }
142
+ end = start + biggest - 1n;
143
+ } else {
144
+ start = part.end / biggest * biggest;
145
+ end = start + biggest - 1n;
146
+ }
147
+ }
148
+ let parts = [{ start, end }];
149
+ if (start !== part.start) {
150
+ parts = parts.concat(subparts({ start: part.start, end: start - 1n }));
151
+ }
152
+ if (end !== part.end) {
153
+ parts = parts.concat(subparts({ start: end + 1n, end: part.end }));
154
+ }
155
+ return parts;
156
+ }
157
+ function diff(a, b) {
158
+ a += 1n;
159
+ return a - b;
160
+ }
161
+ function formatPart(part, version) {
162
+ const ip = normalizeCidr(stringifyIp({ number: BigInt(part.start.toString()), version }));
163
+ const zeroes = diff(part.end, part.start).toString(2);
164
+ const prefix = bits[version] - (zeroes.match(/0/g) || []).length;
165
+ return `${ip}/${prefix}`;
166
+ }
167
+ function mapNets(nets) {
168
+ const maps = { 4: {}, 6: {} };
169
+ for (const { start, end, version } of nets) {
170
+ if (!maps[version][String(start)])
171
+ maps[version][String(start)] = {};
172
+ if (!maps[version][String(end)])
173
+ maps[version][String(end)] = {};
174
+ if (maps[version][String(start)].start) {
175
+ maps[version][String(start)].start += 1;
176
+ } else {
177
+ maps[version][String(start)].start = 1;
178
+ }
179
+ if (maps[version][String(end)].end) {
180
+ maps[version][String(end)].end += 1;
181
+ } else {
182
+ maps[version][String(end)].end = 1;
183
+ }
184
+ }
185
+ return maps;
186
+ }
187
+ function doMerge(maps) {
188
+ let start = null;
189
+ let end = null;
190
+ const numbers = Object.keys(maps);
191
+ let depth = 0;
192
+ const merged = [];
193
+ for (const [index, number] of numbers.entries()) {
194
+ const marker = maps[String(number)];
195
+ if (start === null && marker.start) {
196
+ start = BigInt(number);
197
+ }
198
+ if (marker.end) {
199
+ end = BigInt(number);
200
+ }
201
+ if (marker.start)
202
+ depth += marker.start;
203
+ if (marker.end)
204
+ depth -= marker.end;
205
+ const next = numbers[index + 1];
206
+ if (marker.end && depth === 0 && next && BigInt(next) - BigInt(number) > 1) {
207
+ for (const sub of subparts({ start, end })) {
208
+ merged.push(sub);
209
+ }
210
+ start = null;
211
+ end = null;
212
+ } else if (index === numbers.length - 1) {
213
+ for (const sub of subparts({ start, end })) {
214
+ merged.push(sub);
215
+ }
216
+ }
217
+ }
218
+ return merged;
219
+ }
220
+ function mergeCidr(nets) {
221
+ const arr = uniq((Array.isArray(nets) ? nets : [nets]).sort(compare).map(parseCidr));
222
+ const maps = mapNets(arr);
223
+ const merged = { 4: [], 6: [] };
224
+ for (const v of [4, 6]) {
225
+ merged[v] = doMerge(maps[v]).map((part) => formatPart(part, v));
226
+ }
227
+ return [...merged[4].sort(compare), ...merged[6].sort(compare)];
228
+ }
229
+ function excludeCidr(base, excl) {
230
+ const basenets = mergeCidr(uniq(Array.isArray(base) ? base : [base]));
231
+ const exclnets = mergeCidr(uniq(Array.isArray(excl) ? excl : [excl]));
232
+ const bases = { 4: [], 6: [] };
233
+ const excls = { 4: [], 6: [] };
234
+ for (const basenet of basenets) {
235
+ const version = cidrVersion(basenet);
236
+ if (version)
237
+ bases[version].push(basenet);
238
+ }
239
+ for (const exclnet of exclnets) {
240
+ const version = cidrVersion(exclnet);
241
+ if (version)
242
+ excls[version].push(exclnet);
243
+ }
244
+ for (const v of [4, 6]) {
245
+ for (const exclcidr of excls[v]) {
246
+ for (const [index, basecidr] of bases[v].entries()) {
247
+ const base2 = parseCidr(basecidr);
248
+ const excl2 = parseCidr(exclcidr);
249
+ const remainders = excludeNets(base2, excl2, v);
250
+ if (base2.cidr !== remainders.toString()) {
251
+ bases[v] = bases[v].concat(remainders);
252
+ bases[v].splice(index, 1);
253
+ }
254
+ }
255
+ }
256
+ }
257
+ return bases[4].concat(bases[6]).sort(compare);
258
+ }
259
+ function expandCidr(nets) {
260
+ const arr = uniq(Array.isArray(nets) ? nets : [nets]);
261
+ const ips = [];
262
+ for (const net of mergeCidr(arr)) {
263
+ const { start, end, version } = parseCidr(net);
264
+ for (let number = start; number <= end; number++) {
265
+ ips.push(stringifyIp({ number, version }));
266
+ }
267
+ }
268
+ return ips.map((ip) => normalizeCidr(ip));
269
+ }
270
+ function overlapCidr(a, b) {
271
+ const aNets = uniq(Array.isArray(a) ? a : [a]);
272
+ const bNets = uniq(Array.isArray(b) ? b : [b]);
273
+ for (const a2 of aNets) {
274
+ const aParsed = parseCidr(a2);
275
+ for (const b2 of bNets) {
276
+ const bParsed = parseCidr(b2);
277
+ if (aParsed.version !== bParsed.version) {
278
+ continue;
279
+ }
280
+ if (doNetsOverlap(aParsed, bParsed)) {
281
+ return true;
282
+ }
283
+ }
284
+ }
285
+ return false;
286
+ }
287
+ function containsCidr(a, b) {
288
+ const aNets = uniq(Array.isArray(a) ? a : [a]);
289
+ const bNets = uniq(Array.isArray(b) ? b : [b]);
290
+ const numExpected = bNets.length;
291
+ let numFound = 0;
292
+ for (const a2 of aNets) {
293
+ const aParsed = parseCidr(a2);
294
+ for (const b2 of bNets) {
295
+ const bParsed = parseCidr(b2);
296
+ if (aParsed.version !== bParsed.version) {
297
+ continue;
298
+ }
299
+ if (netContains(aParsed, bParsed)) {
300
+ numFound++;
301
+ continue;
302
+ }
303
+ }
304
+ }
305
+ return numFound === numExpected;
306
+ }
307
+ const index = {
308
+ mergeCidr,
309
+ excludeCidr,
310
+ expandCidr,
311
+ overlapCidr,
312
+ containsCidr,
313
+ normalizeCidr,
314
+ parseCidr
315
+ };
316
+
317
+ export { containsCidr, index as default, excludeCidr, expandCidr, mergeCidr, normalizeCidr, overlapCidr, parseCidr };
package/package.json CHANGED
@@ -1,31 +1,35 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "9.1.1",
3
+ "version": "10.0.1",
4
4
  "author": "silverwind <me@silverwind.io>",
5
5
  "description": "Tools to work with IPv4 and IPv6 CIDR",
6
6
  "repository": "silverwind/cidr-tools",
7
7
  "license": "BSD-2-Clause",
8
8
  "type": "module",
9
- "exports": "./index.js",
10
- "types": "index.d.ts",
11
9
  "sideEffects": false,
10
+ "main": "./dist/index.js",
11
+ "exports": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
12
16
  "engines": {
13
17
  "node": ">=18"
14
18
  },
15
- "files": [
16
- "index.js",
17
- "index.d.ts"
18
- ],
19
19
  "dependencies": {
20
- "ip-bigint": "^8.0.2"
20
+ "ip-bigint": "^8.1.0"
21
21
  },
22
22
  "devDependencies": {
23
- "eslint": "8.56.0",
24
- "eslint-config-silverwind": "80.0.3",
25
- "tsd": "0.30.5",
26
- "updates": "15.1.2",
27
- "versions": "12.0.1",
28
- "vitest": "1.3.1",
29
- "vitest-config-silverwind": "5.1.1"
23
+ "@types/node": "20.12.12",
24
+ "eslint": "8.57.0",
25
+ "eslint-config-silverwind": "85.1.5",
26
+ "eslint-config-silverwind-typescript": "3.2.8",
27
+ "typescript-config-silverwind": "4.4.0",
28
+ "updates": "16.1.1",
29
+ "versions": "12.1.2",
30
+ "vite": "5.2.12",
31
+ "vite-config-silverwind": "2.1.4",
32
+ "vitest": "1.6.0",
33
+ "vitest-config-silverwind": "9.0.6"
30
34
  }
31
35
  }
package/index.d.ts DELETED
@@ -1,39 +0,0 @@
1
- type IPv4Address = string;
2
- type IPv4CIDR = string;
3
- type IPv6Address = string;
4
- type IPv6CIDR = string;
5
- type Network = IPv4Address | IPv4CIDR | IPv6Address | IPv6CIDR;
6
- type Networks = Network | Network[];
7
-
8
- type Parsed = {
9
- cidr: string;
10
- ip: string;
11
- version: number;
12
- prefix: string;
13
- start: bigint;
14
- end: bigint;
15
- };
16
-
17
- type NormalizeOpts = {
18
- compress?: boolean;
19
- hexify?: boolean;
20
- };
21
-
22
- export function mergeCidr(networks: Networks): Network[];
23
- export function excludeCidr(baseNetworks: Networks, excludeNetworks: Networks): Network[];
24
- export function expandCidr(networks: Networks): Network[];
25
- export function overlapCidr(networksA: Networks, networksB: Networks): boolean;
26
- export function normalizeCidr(cidr: Networks, opts?: NormalizeOpts): Networks;
27
- export function containsCidr(networksA: Networks, networksB: Networks): boolean;
28
- export function parseCidr(network: Network): Parsed;
29
-
30
- declare const _default: {
31
- mergeCidr: typeof mergeCidr;
32
- excludeCidr: typeof excludeCidr;
33
- expandCidr: typeof expandCidr;
34
- overlapCidr: typeof overlapCidr;
35
- normalizeCidr: typeof normalizeCidr;
36
- containsCidr: typeof containsCidr;
37
- parseCidr: typeof parseCidr;
38
- };
39
- export default _default;
package/index.js DELETED
@@ -1,413 +0,0 @@
1
- import {parseIp, stringifyIp, normalizeIp, ipVersion} from "ip-bigint";
2
-
3
- const bits = {4: 32, 6: 128};
4
- const uniq = arr => Array.from(new Set(arr));
5
- const cidrVersion = cidr => cidr.includes("/") ? ipVersion(cidr) : 0;
6
-
7
- // TODO: pass parsed objects in here
8
- function compare(a, b) {
9
- const {number: aNum, version: aVersion} = parseIp(a.replace(/\/.+/, ""));
10
- const {number: bNum, version: bVersion} = parseIp(b.replace(/\/.+/, ""));
11
- if (aVersion === bVersion) {
12
- return aNum - bNum > 0n ? 1 : aNum - bNum < 0n ? -1 : 0;
13
- } else {
14
- return aVersion > bVersion;
15
- }
16
- }
17
-
18
- function doNormalize(cidr, {compress = true, hexify = false} = {}) {
19
- const {start, end, prefix, version} = parseCidr(cidr);
20
- if (start !== end) { // cidr
21
- // set network address to first address
22
- const ip = normalizeIp(stringifyIp({number: start, version}), {compress, hexify});
23
- return `${ip}/${prefix}`;
24
- } else { // single ip
25
- return normalizeIp(cidr, {compress, hexify});
26
- }
27
- }
28
-
29
- export function normalizeCidr(cidr, {compress = true, hexify = false} = {}) {
30
- if (Array.isArray(cidr)) {
31
- return cidr.map(entry => normalizeCidr(entry, {compress, hexify}));
32
- } else {
33
- return doNormalize(cidr, {compress, hexify});
34
- }
35
- }
36
-
37
- export function parseCidr(str) {
38
- const cidrVer = cidrVersion(str);
39
- const parsed = Object.create(null);
40
-
41
- let cidr;
42
- if (cidrVer) {
43
- cidr = str;
44
- parsed.version = cidrVer;
45
- } else {
46
- const version = ipVersion(str);
47
- if (version) {
48
- cidr = `${str}/${bits[version]}`;
49
- parsed.version = version;
50
- } else {
51
- throw new Error(`Network is not a CIDR or IP: ${str}`);
52
- }
53
- }
54
-
55
- const [ipAndMisc, prefix] = cidr.split("/");
56
-
57
- if (!/^[0-9]+$/.test(prefix)) {
58
- throw new Error(`Network is not a CIDR or IP: ${str}`);
59
- }
60
-
61
- const {number, version, ipv4mapped, scopeid} = parseIp(ipAndMisc);
62
- parsed.ip = stringifyIp({number, version, ipv4mapped, scopeid});
63
- parsed.cidr = `${parsed.ip}/${prefix}`;
64
- parsed.prefix = prefix;
65
-
66
- const numBits = bits[version];
67
- const ipBits = number.toString(2).padStart(numBits, "0");
68
- const prefixLen = Number(numBits - prefix);
69
- const startBits = ipBits.substring(0, numBits - prefixLen);
70
- parsed.start = BigInt(`0b${startBits}${"0".repeat(prefixLen)}`);
71
- parsed.end = BigInt(`0b${startBits}${"1".repeat(prefixLen)}`);
72
- return parsed;
73
- }
74
-
75
- // returns whether networks fully or partially overlap
76
- function doNetsOverlap(a, b) {
77
- // aaa
78
- // bbb
79
- if (a.start > b.end) return false; // a starts after b
80
-
81
- // aaa
82
- // bbb
83
- if (b.start > a.end) return false; // b starts after a
84
-
85
- return true;
86
- }
87
-
88
- // returns whether network a fully contains network b;
89
- function netContains(a, b) {
90
- // aaa
91
- // bbbb
92
- if (b.start < a.start) return false; // a starts after b
93
-
94
- // aaa
95
- // bbbb
96
- if (b.end > a.end) return false; // b starts after a
97
-
98
- return true;
99
- }
100
-
101
- // exclude b from a and return remainder cidrs
102
- function excludeNets(a, b, v) {
103
- const parts = [];
104
-
105
- // compareTo returns negative if left is less than right
106
-
107
- // aaa
108
- // bbb
109
- // aaa
110
- // bbb
111
- if (a.start > b.end || a.end < b.start) {
112
- return [a.cidr];
113
- }
114
-
115
- // aaa
116
- // bbb
117
- if (a.start === b.start && a.end === b.end) {
118
- return [];
119
- }
120
-
121
- // aa
122
- // bbbb
123
- if (a.start > b.start && a.end < b.end) {
124
- return [];
125
- }
126
-
127
- // aaaa
128
- // bbbb
129
- // aaaa
130
- // bb
131
- if (a.start < b.start && a.end <= b.end) {
132
- parts.push({start: a.start, end: b.start - 1n});
133
- }
134
-
135
- // aaa
136
- // bbb
137
- // aaaa
138
- // bbb
139
- if (a.start >= b.start && a.end > b.end) {
140
- parts.push({start: b.end + 1n, end: a.end});
141
- }
142
-
143
- // aaaa
144
- // bb
145
- if (a.start < b.start && a.end > b.end) {
146
- parts.push(
147
- {start: a.start, end: b.start - 1n},
148
- {start: b.end + 1n, end: a.end},
149
- );
150
- }
151
-
152
- const remaining = [];
153
- for (const part of parts) {
154
- for (const subpart of subparts(part)) {
155
- remaining.push(formatPart(subpart, v));
156
- }
157
- }
158
-
159
- return mergeCidr(remaining);
160
- }
161
-
162
- function biggestPowerOfTwo(num) {
163
- if (num === 0n) return 0n;
164
- return 2n ** BigInt(String(num.toString(2).length - 1));
165
- }
166
-
167
- function subparts(part) {
168
- // special case for when part is length 1
169
- if ((part.end - part.start) === 1n) {
170
- if (part.end % 2n === 0n) {
171
- return [{start: part.start, end: part.start}, {start: part.end, end: part.end}];
172
- } else {
173
- return [{start: part.start, end: part.end}];
174
- }
175
- }
176
-
177
- const size = diff(part.end, part.start);
178
- let biggest = biggestPowerOfTwo(size);
179
-
180
- let start, end;
181
- if (size === biggest && part.start + size === part.end) {
182
- return [part];
183
- } else if (part.start % biggest === 0n) {
184
- // start is matching on the size-defined boundary - ex: 0-12, use 0-8
185
- start = part.start;
186
- end = start + biggest - 1n;
187
- } else {
188
- start = (part.end / biggest) * biggest;
189
-
190
- // start is not matching on the size-defined boundary - 4-16, use 8-16
191
- if ((start + biggest - 1n) > part.end) {
192
- // divide will floor to nearest integer
193
- start = ((part.end / biggest) - 1n) * biggest;
194
-
195
- while (start < part.start) {
196
- biggest /= 2n;
197
- start = ((part.end / biggest) - 1n) * biggest;
198
- }
199
-
200
- end = start + biggest - 1n;
201
- } else {
202
- start = (part.end / biggest) * biggest;
203
- end = start + biggest - 1n;
204
- }
205
- }
206
-
207
- let parts = [{start, end}];
208
-
209
- // additional subnets on left side
210
- if (start !== part.start) {
211
- parts = parts.concat(subparts({start: part.start, end: start - 1n}));
212
- }
213
-
214
- // additional subnets on right side
215
- if (end !== part.end) {
216
- parts = parts.concat(subparts({start: end + 1n, end: part.end}));
217
- }
218
-
219
- return parts;
220
- }
221
-
222
- function diff(a, b) {
223
- if (typeof a !== "bigint") a = BigInt(a);
224
- if (typeof b !== "bigint") b = BigInt(b);
225
- a += 1n;
226
- return a - b;
227
- }
228
-
229
- function formatPart(part, version) {
230
- const ip = normalizeCidr(stringifyIp({number: BigInt(part.start.toString()), version}));
231
- const zeroes = diff(part.end, part.start).toString(2);
232
- const prefix = bits[version] - (zeroes.match(/0/g) || []).length;
233
- return `${ip}/${prefix}`;
234
- }
235
-
236
- function mapNets(nets) {
237
- const maps = {4: {}, 6: {}}; // TODO: use Map with BigInt key
238
- for (const {start, end, version} of nets) {
239
- if (!maps[version][start]) maps[version][start] = {};
240
- if (!maps[version][end]) maps[version][end] = {};
241
-
242
- if (maps[version][start].start) {
243
- maps[version][start].start += 1;
244
- } else {
245
- maps[version][start].start = 1;
246
- }
247
-
248
- if (maps[version][end].end) {
249
- maps[version][end].end += 1;
250
- } else {
251
- maps[version][end].end = 1;
252
- }
253
- }
254
- return maps;
255
- }
256
-
257
- function doMerge(maps) {
258
- let start = null;
259
- let end = null;
260
- const numbers = Object.keys(maps);
261
- let depth = 0;
262
- const merged = [];
263
-
264
- for (const [index, number] of numbers.entries()) {
265
- const marker = maps[number];
266
-
267
- if (start === null && marker.start) {
268
- start = BigInt(number);
269
- }
270
- if (marker.end) {
271
- end = BigInt(number);
272
- }
273
-
274
- if (marker.start) depth += marker.start;
275
- if (marker.end) depth -= marker.end;
276
-
277
- const next = numbers[index + 1];
278
- if (marker.end && depth === 0 && next && ((BigInt(next) - BigInt(number)) > 1)) {
279
- // when there is a end and the next part is more than one number away, we cut a part
280
- for (const sub of subparts({start, end})) {
281
- merged.push(sub);
282
- }
283
- start = null;
284
- end = null;
285
- } else if (index === (numbers.length - 1)) {
286
- // cut the final part
287
- for (const sub of subparts({start, end})) {
288
- merged.push(sub);
289
- }
290
- }
291
- }
292
- return merged;
293
- }
294
-
295
- export function mergeCidr(nets) {
296
- // sort to workaround https://github.com/silverwind/cidr-tools/issues/17
297
- nets = uniq((Array.isArray(nets) ? nets : [nets]).sort(compare).map(parseCidr));
298
- const maps = mapNets(nets);
299
-
300
- const merged = {4: [], 6: []};
301
- for (const v of [4, 6]) {
302
- merged[v] = doMerge(maps[v]).map(part => formatPart(part, v));
303
- }
304
-
305
- return [...merged[4].sort(compare), ...merged[6].sort(compare)];
306
- }
307
-
308
- export function excludeCidr(basenets, exclnets) {
309
- basenets = uniq(Array.isArray(basenets) ? basenets : [basenets]);
310
- exclnets = uniq(Array.isArray(exclnets) ? exclnets : [exclnets]);
311
-
312
- basenets = mergeCidr(basenets);
313
- exclnets = mergeCidr(exclnets);
314
-
315
- const bases = {4: [], 6: []};
316
- const excls = {4: [], 6: []};
317
-
318
- for (const basenet of basenets) {
319
- bases[cidrVersion(basenet)].push(basenet);
320
- }
321
-
322
- for (const exclnet of exclnets) {
323
- excls[cidrVersion(exclnet)].push(exclnet);
324
- }
325
-
326
- for (const v of [4, 6]) {
327
- for (const exclcidr of excls[v]) {
328
- for (const [index, basecidr] of bases[v].entries()) {
329
- const base = parseCidr(basecidr);
330
- const excl = parseCidr(exclcidr);
331
- const remainders = excludeNets(base, excl, v);
332
- if (base.cidr !== remainders.toString()) {
333
- bases[v] = bases[v].concat(remainders);
334
- bases[v].splice(index, 1);
335
- }
336
- }
337
- }
338
- }
339
-
340
- return bases[4].concat(bases[6]).sort(compare);
341
- }
342
-
343
- export function expandCidr(nets) {
344
- nets = uniq(Array.isArray(nets) ? nets : [nets]);
345
-
346
- const ips = [];
347
- for (const net of mergeCidr(nets)) {
348
- const {start, end, version} = parseCidr(net);
349
- for (let number = start; number <= end; number++) {
350
- ips.push(stringifyIp({number, version}));
351
- }
352
- }
353
- return ips.map(normalizeCidr);
354
- }
355
-
356
- export function overlapCidr(a, b) {
357
- const aNets = uniq(Array.isArray(a) ? a : [a]);
358
- const bNets = uniq(Array.isArray(b) ? b : [b]);
359
-
360
- for (const a of aNets) {
361
- const aParsed = parseCidr(a);
362
- for (const b of bNets) {
363
- const bParsed = parseCidr(b);
364
-
365
- // version mismatch
366
- if (aParsed.version !== bParsed.version) {
367
- continue;
368
- }
369
-
370
- if (doNetsOverlap(aParsed, bParsed)) {
371
- return true;
372
- }
373
- }
374
- }
375
-
376
- return false;
377
- }
378
-
379
- export function containsCidr(a, b) {
380
- const aNets = uniq(Array.isArray(a) ? a : [a]);
381
- const bNets = uniq(Array.isArray(b) ? b : [b]);
382
-
383
- const numExpected = bNets.length;
384
- let numFound = 0;
385
- for (const a of aNets) {
386
- const aParsed = parseCidr(a);
387
- for (const b of bNets) {
388
- const bParsed = parseCidr(b);
389
-
390
- // version mismatch
391
- if (aParsed.version !== bParsed.version) {
392
- continue;
393
- }
394
-
395
- if (netContains(aParsed, bParsed)) {
396
- numFound++;
397
- continue;
398
- }
399
- }
400
- }
401
-
402
- return numFound === numExpected;
403
- }
404
-
405
- export default {
406
- mergeCidr,
407
- excludeCidr,
408
- expandCidr,
409
- overlapCidr,
410
- containsCidr,
411
- normalizeCidr,
412
- parseCidr,
413
- };