cidr-tools 12.0.1 → 12.0.3
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/dist/index.d.ts +1 -0
- package/dist/index.js +101 -155
- package/package.json +19 -16
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ declare function parseCidr(str: Network): ParsedCidr;
|
|
|
23
23
|
declare function mergeCidr(nets: Networks): Array<Network>;
|
|
24
24
|
/** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
|
|
25
25
|
declare function excludeCidr(base: Networks, excl: Networks): Array<Network>;
|
|
26
|
+
/** Returns a generator for individual IPs contained in the networks. */
|
|
26
27
|
declare function expandCidr(nets: Networks): Generator<Network>;
|
|
27
28
|
/** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
|
|
28
29
|
declare function overlapCidr(a: Networks, b: Networks): boolean;
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,10 @@ const octetStrings = Array.from({ length: 256 }, (_, i) => String(i));
|
|
|
9
9
|
const octetDotStrings = Array.from({ length: 256 }, (_, i) => `${i}.`);
|
|
10
10
|
const prefixStrings = Array.from({ length: 129 }, (_, i) => `/${i}`);
|
|
11
11
|
const prefixNumStrings = Array.from({ length: 129 }, (_, i) => String(i));
|
|
12
|
+
const hexStrings = Array.from({ length: 256 }, (_, i) => i.toString(16));
|
|
13
|
+
const hexPadStrings = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
14
|
+
const hostMasks = Array.from({ length: 129 }, (_, i) => (1n << BigInt(i)) - 1n);
|
|
15
|
+
const hostNotMasks = hostMasks.map((mask) => ~mask);
|
|
12
16
|
const cmpV4StartEnd = (a, b) => a.start - b.start || a.end - b.end;
|
|
13
17
|
const cmpV4Start = (a, b) => a.start - b.start;
|
|
14
18
|
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;
|
|
@@ -73,28 +77,37 @@ function doNormalize(cidr, opts) {
|
|
|
73
77
|
const ip = formatIPv4Fast(rangeV4Start);
|
|
74
78
|
return rangeSlashIndex !== -1 ? ip + prefixStrings[rangeV4Prefix] : ip;
|
|
75
79
|
}
|
|
76
|
-
const
|
|
80
|
+
const slashIndex = rangeSlashIndex;
|
|
81
|
+
const prefixPresent = slashIndex !== -1;
|
|
82
|
+
let prefixNum = prefixPresent ? parsePrefixNum(cidr, slashIndex) : -1;
|
|
83
|
+
const { number, version } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr);
|
|
84
|
+
if (prefixNum === -1) prefixNum = bits[version];
|
|
77
85
|
if (version === 4) {
|
|
78
|
-
const
|
|
79
|
-
|
|
86
|
+
const hostBits = 32 - prefixNum;
|
|
87
|
+
let startNum = Number(number);
|
|
88
|
+
if (hostBits >= 32) startNum = 0;
|
|
89
|
+
else if (hostBits > 0) startNum = (startNum & ~((1 << hostBits >>> 0) - 1)) >>> 0;
|
|
90
|
+
const ip = formatIPv4Fast(startNum);
|
|
91
|
+
return hostBits > 0 || prefixPresent ? ip + prefixStrings[prefixNum] : ip;
|
|
80
92
|
}
|
|
81
93
|
const { compress = true, hexify = false } = opts || {};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
version
|
|
85
|
-
}), {
|
|
94
|
+
const hostBits = 128 - prefixNum;
|
|
95
|
+
if (hostBits <= 0 && !prefixPresent) return normalizeIp(cidr, {
|
|
86
96
|
compress,
|
|
87
97
|
hexify
|
|
88
|
-
})
|
|
89
|
-
|
|
98
|
+
});
|
|
99
|
+
const ip = stringifyIp({
|
|
100
|
+
number: hostBits > 0 ? number & hostNotMasks[hostBits] : number,
|
|
101
|
+
version
|
|
102
|
+
});
|
|
103
|
+
return (compress ? ip : normalizeIp(ip, {
|
|
90
104
|
compress,
|
|
91
105
|
hexify
|
|
92
|
-
});
|
|
106
|
+
})) + prefixStrings[prefixNum];
|
|
93
107
|
}
|
|
94
108
|
/** 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. */
|
|
95
109
|
function normalizeCidr(cidr, opts) {
|
|
96
|
-
|
|
97
|
-
else return doNormalize(cidr, opts);
|
|
110
|
+
return Array.isArray(cidr) ? cidr.map((entry) => doNormalize(entry, opts)) : doNormalize(cidr, opts);
|
|
98
111
|
}
|
|
99
112
|
/** 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. */
|
|
100
113
|
function parseCidr(str) {
|
|
@@ -130,7 +143,8 @@ function parseCidr(str) {
|
|
|
130
143
|
let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
|
|
131
144
|
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
|
|
132
145
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
133
|
-
|
|
146
|
+
const numBits = bits[version];
|
|
147
|
+
if (prefixNum === -1) prefixNum = numBits;
|
|
134
148
|
const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
|
|
135
149
|
const ip = stringifyIp({
|
|
136
150
|
number,
|
|
@@ -138,16 +152,21 @@ function parseCidr(str) {
|
|
|
138
152
|
ipv4mapped,
|
|
139
153
|
scopeid
|
|
140
154
|
});
|
|
141
|
-
const hostBits =
|
|
142
|
-
|
|
155
|
+
const hostBits = numBits - prefixNum;
|
|
156
|
+
let start = number;
|
|
157
|
+
let end = number;
|
|
158
|
+
if (hostBits > 0) {
|
|
159
|
+
start = number & hostNotMasks[hostBits];
|
|
160
|
+
end = number | hostMasks[hostBits];
|
|
161
|
+
}
|
|
143
162
|
return {
|
|
144
163
|
cidr: ip + prefixStrings[prefixNum],
|
|
145
164
|
ip,
|
|
146
165
|
version,
|
|
147
166
|
prefix,
|
|
148
167
|
prefixPresent,
|
|
149
|
-
start
|
|
150
|
-
end
|
|
168
|
+
start,
|
|
169
|
+
end
|
|
151
170
|
};
|
|
152
171
|
}
|
|
153
172
|
function parseCidrLean(str) {
|
|
@@ -161,8 +180,9 @@ function parseCidrLean(str) {
|
|
|
161
180
|
let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
|
|
162
181
|
const { number, version } = parseIp(ipPart);
|
|
163
182
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
164
|
-
|
|
165
|
-
|
|
183
|
+
const numBits = bits[version];
|
|
184
|
+
if (prefixNum === -1) prefixNum = numBits;
|
|
185
|
+
const hostBits = numBits - prefixNum;
|
|
166
186
|
if (version === 4) {
|
|
167
187
|
const num = Number(number);
|
|
168
188
|
if (hostBits >= 32) return {
|
|
@@ -177,10 +197,14 @@ function parseCidrLean(str) {
|
|
|
177
197
|
version: 4
|
|
178
198
|
};
|
|
179
199
|
}
|
|
180
|
-
|
|
200
|
+
if (hostBits <= 0) return {
|
|
201
|
+
start: number,
|
|
202
|
+
end: number,
|
|
203
|
+
version: 6
|
|
204
|
+
};
|
|
181
205
|
return {
|
|
182
|
-
start: number &
|
|
183
|
-
end: number |
|
|
206
|
+
start: number & hostNotMasks[hostBits],
|
|
207
|
+
end: number | hostMasks[hostBits],
|
|
184
208
|
version: 6
|
|
185
209
|
};
|
|
186
210
|
}
|
|
@@ -207,117 +231,34 @@ function biggestPowerOfTwo4(num) {
|
|
|
207
231
|
return 1 << 31 - Math.clz32(num) >>> 0;
|
|
208
232
|
}
|
|
209
233
|
function subparts4(pStart, pEnd, output) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
if (pEnd - pStart === 1) {
|
|
219
|
-
if (pEnd % 2 === 0) output.push({
|
|
220
|
-
start: pStart,
|
|
221
|
-
end: pStart
|
|
222
|
-
}, {
|
|
223
|
-
start: pEnd,
|
|
224
|
-
end: pEnd
|
|
225
|
-
});
|
|
226
|
-
else output.push({
|
|
227
|
-
start: pStart,
|
|
228
|
-
end: pEnd
|
|
229
|
-
});
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
const size = pEnd - pStart + 1;
|
|
233
|
-
if ((size & size - 1) === 0 && pStart % size === 0) {
|
|
234
|
-
output.push({
|
|
235
|
-
start: pStart,
|
|
236
|
-
end: pEnd
|
|
237
|
-
});
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
let biggest = biggestPowerOfTwo4(size);
|
|
241
|
-
let start;
|
|
242
|
-
if (pStart % biggest === 0) start = pStart;
|
|
243
|
-
else {
|
|
244
|
-
start = Math.floor(pEnd / biggest) * biggest;
|
|
245
|
-
if (start + biggest - 1 > pEnd) {
|
|
246
|
-
start -= biggest;
|
|
247
|
-
while (start < pStart) {
|
|
248
|
-
biggest /= 2;
|
|
249
|
-
start = (Math.floor(pEnd / biggest) - 1) * biggest;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
234
|
+
let start = pStart;
|
|
235
|
+
while (start <= pEnd) {
|
|
236
|
+
const size = pEnd - start + 1;
|
|
237
|
+
const lowBit = (start & -start) >>> 0;
|
|
238
|
+
const blockSize = lowBit !== 0 && lowBit <= size ? lowBit : biggestPowerOfTwo4(size);
|
|
239
|
+
output.push(formatIPv4Fast(start) + prefixStrings[Math.clz32(blockSize - 1)]);
|
|
240
|
+
start += blockSize;
|
|
252
241
|
}
|
|
253
|
-
const end = start + biggest - 1;
|
|
254
|
-
if (start !== pStart) subparts4(pStart, start - 1, output);
|
|
255
|
-
output.push({
|
|
256
|
-
start,
|
|
257
|
-
end
|
|
258
|
-
});
|
|
259
|
-
if (end !== pEnd) subparts4(end + 1, pEnd, output);
|
|
260
242
|
}
|
|
261
243
|
function subparts6(pStart, pEnd, output) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
start: pStart,
|
|
273
|
-
end: pStart
|
|
274
|
-
}, {
|
|
275
|
-
start: pEnd,
|
|
276
|
-
end: pEnd
|
|
277
|
-
});
|
|
278
|
-
else output.push({
|
|
279
|
-
start: pStart,
|
|
280
|
-
end: pEnd
|
|
281
|
-
});
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
const size = pEnd - pStart + 1n;
|
|
285
|
-
if ((size & size - 1n) === 0n && (pStart & size - 1n) === 0n) {
|
|
286
|
-
output.push({
|
|
287
|
-
start: pStart,
|
|
288
|
-
end: pEnd
|
|
289
|
-
});
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
let biggest = biggestPowerOfTwo(size);
|
|
293
|
-
let start;
|
|
294
|
-
if ((pStart & biggest - 1n) === 0n) start = pStart;
|
|
295
|
-
else {
|
|
296
|
-
start = pEnd & -biggest;
|
|
297
|
-
if (start + biggest - 1n > pEnd) {
|
|
298
|
-
start -= biggest;
|
|
299
|
-
while (start < pStart) {
|
|
300
|
-
biggest >>= 1n;
|
|
301
|
-
start = (pEnd & -biggest) - biggest;
|
|
302
|
-
}
|
|
244
|
+
let start = pStart;
|
|
245
|
+
while (start <= pEnd) {
|
|
246
|
+
const size = pEnd - start + 1n;
|
|
247
|
+
const lowBit = start & -start;
|
|
248
|
+
if ((size & size - 1n) === 0n && (lowBit === 0n || lowBit >= size)) {
|
|
249
|
+
output.push(stringifyIp({
|
|
250
|
+
number: start,
|
|
251
|
+
version: 6
|
|
252
|
+
}) + prefixStrings[129 - bigintBitLength(size)]);
|
|
253
|
+
return;
|
|
303
254
|
}
|
|
255
|
+
const blockSize = lowBit !== 0n && lowBit <= size ? lowBit : biggestPowerOfTwo(size);
|
|
256
|
+
output.push(stringifyIp({
|
|
257
|
+
number: start,
|
|
258
|
+
version: 6
|
|
259
|
+
}) + prefixStrings[129 - bigintBitLength(blockSize)]);
|
|
260
|
+
start += blockSize;
|
|
304
261
|
}
|
|
305
|
-
const end = start + biggest - 1n;
|
|
306
|
-
if (start !== pStart) subparts6(pStart, start - 1n, output);
|
|
307
|
-
output.push({
|
|
308
|
-
start,
|
|
309
|
-
end
|
|
310
|
-
});
|
|
311
|
-
if (end !== pEnd) subparts6(end + 1n, pEnd, output);
|
|
312
|
-
}
|
|
313
|
-
function formatPart4(part) {
|
|
314
|
-
return formatIPv4Fast(part.start) + prefixStrings[Math.clz32(part.end - part.start)];
|
|
315
|
-
}
|
|
316
|
-
function formatPart6(part) {
|
|
317
|
-
return stringifyIp({
|
|
318
|
-
number: part.start,
|
|
319
|
-
version: 6
|
|
320
|
-
}) + prefixStrings[128 - bigintBitLength(part.end - part.start)];
|
|
321
262
|
}
|
|
322
263
|
function mergeIntervalsRaw4(nets) {
|
|
323
264
|
if (nets.length === 0) return [];
|
|
@@ -369,16 +310,6 @@ function mergeIntervalsRaw6(nets) {
|
|
|
369
310
|
});
|
|
370
311
|
return merged;
|
|
371
312
|
}
|
|
372
|
-
function mergeIntervals4(nets) {
|
|
373
|
-
const merged = [];
|
|
374
|
-
for (const part of mergeIntervalsRaw4(nets)) subparts4(part.start, part.end, merged);
|
|
375
|
-
return merged;
|
|
376
|
-
}
|
|
377
|
-
function mergeIntervals6(nets) {
|
|
378
|
-
const merged = [];
|
|
379
|
-
for (const part of mergeIntervalsRaw6(nets)) subparts6(part.start, part.end, merged);
|
|
380
|
-
return merged;
|
|
381
|
-
}
|
|
382
313
|
function subtractSorted4(bases, excls) {
|
|
383
314
|
if (excls.length === 0) return bases;
|
|
384
315
|
if (bases.length === 0) return [];
|
|
@@ -440,8 +371,8 @@ function mergeCidr(nets) {
|
|
|
440
371
|
else v6.push(n);
|
|
441
372
|
}
|
|
442
373
|
const merged = [];
|
|
443
|
-
for (const part of
|
|
444
|
-
for (const part of
|
|
374
|
+
for (const part of mergeIntervalsRaw4(v4)) subparts4(part.start, part.end, merged);
|
|
375
|
+
for (const part of mergeIntervalsRaw6(v6)) subparts6(part.start, part.end, merged);
|
|
445
376
|
return merged;
|
|
446
377
|
}
|
|
447
378
|
/** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
|
|
@@ -462,19 +393,18 @@ function excludeCidr(base, excl) {
|
|
|
462
393
|
}
|
|
463
394
|
const result = [];
|
|
464
395
|
{
|
|
465
|
-
const
|
|
466
|
-
const
|
|
467
|
-
for (const part of
|
|
468
|
-
for (const p of aligned) result.push(formatPart4(p));
|
|
396
|
+
const baseParts = mergeIntervalsRaw4(v4base);
|
|
397
|
+
const exclParts = mergeIntervalsRaw4(v4excl);
|
|
398
|
+
for (const part of subtractSorted4(baseParts, exclParts)) subparts4(part.start, part.end, result);
|
|
469
399
|
}
|
|
470
400
|
{
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
for (const part of
|
|
474
|
-
for (const p of aligned) result.push(formatPart6(p));
|
|
401
|
+
const baseParts = mergeIntervalsRaw6(v6base);
|
|
402
|
+
const exclParts = mergeIntervalsRaw6(v6excl);
|
|
403
|
+
for (const part of subtractSorted6(baseParts, exclParts)) subparts6(part.start, part.end, result);
|
|
475
404
|
}
|
|
476
405
|
return result;
|
|
477
406
|
}
|
|
407
|
+
/** Returns a generator for individual IPs contained in the networks. */
|
|
478
408
|
function* expandCidr(nets) {
|
|
479
409
|
const arr = Array.isArray(nets) ? nets : [nets];
|
|
480
410
|
const v4 = [];
|
|
@@ -487,13 +417,13 @@ function* expandCidr(nets) {
|
|
|
487
417
|
if (v4.length > 0) for (const part of mergeIntervalsRaw4(v4)) {
|
|
488
418
|
let prevHigh = -1;
|
|
489
419
|
let prefix = "";
|
|
490
|
-
for (let
|
|
491
|
-
const high =
|
|
420
|
+
for (let num = part.start; num <= part.end; num++) {
|
|
421
|
+
const high = num >>> 8;
|
|
492
422
|
if (high !== prevHigh) {
|
|
493
|
-
prefix = octetDotStrings[
|
|
423
|
+
prefix = octetDotStrings[num >>> 24 & 255] + octetDotStrings[num >>> 16 & 255] + octetDotStrings[num >>> 8 & 255];
|
|
494
424
|
prevHigh = high;
|
|
495
425
|
}
|
|
496
|
-
yield prefix + octetStrings[
|
|
426
|
+
yield prefix + octetStrings[num & 255];
|
|
497
427
|
}
|
|
498
428
|
}
|
|
499
429
|
if (v6.length > 0) {
|
|
@@ -501,9 +431,25 @@ function* expandCidr(nets) {
|
|
|
501
431
|
number: 0n,
|
|
502
432
|
version: 6
|
|
503
433
|
};
|
|
504
|
-
for (const part of mergeIntervalsRaw6(v6))
|
|
505
|
-
|
|
506
|
-
|
|
434
|
+
for (const part of mergeIntervalsRaw6(v6)) {
|
|
435
|
+
let num = part.start;
|
|
436
|
+
while (num <= part.end) {
|
|
437
|
+
const blockStart = num & -65536n;
|
|
438
|
+
const blockEnd = blockStart | 65535n;
|
|
439
|
+
let group = Number(num & 65535n);
|
|
440
|
+
const groupEnd = part.end < blockEnd ? Number(part.end & 65535n) : 65535;
|
|
441
|
+
if (group === 0) {
|
|
442
|
+
ipObj.number = blockStart;
|
|
443
|
+
yield stringifyIp(ipObj);
|
|
444
|
+
group = 1;
|
|
445
|
+
}
|
|
446
|
+
if (group <= groupEnd) {
|
|
447
|
+
ipObj.number = blockStart | 1n;
|
|
448
|
+
const prefix = stringifyIp(ipObj).slice(0, -1);
|
|
449
|
+
for (; group <= groupEnd; group++) yield group < 256 ? prefix + hexStrings[group] : prefix + hexStrings[group >>> 8] + hexPadStrings[group & 255];
|
|
450
|
+
}
|
|
451
|
+
num = blockEnd + 1n;
|
|
452
|
+
}
|
|
507
453
|
}
|
|
508
454
|
}
|
|
509
455
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cidr-tools",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.3",
|
|
4
4
|
"author": "silverwind <me@silverwind.io>",
|
|
5
5
|
"description": "Tools to work with IPv4 and IPv6 CIDR",
|
|
6
6
|
"keywords": [
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
"subnet",
|
|
12
12
|
"network"
|
|
13
13
|
],
|
|
14
|
-
"repository":
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "silverwind/cidr-tools"
|
|
17
|
+
},
|
|
15
18
|
"license": "BSD-2-Clause",
|
|
16
19
|
"type": "module",
|
|
17
20
|
"sideEffects": false,
|
|
@@ -26,23 +29,23 @@
|
|
|
26
29
|
"bun": "*"
|
|
27
30
|
},
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"ip-bigint": "^9.0.
|
|
32
|
+
"ip-bigint": "^9.0.6"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|
|
32
|
-
"@types/node": "25.
|
|
33
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
34
|
-
"@vitest/coverage-v8": "4.1.
|
|
35
|
-
"eslint": "10.4.
|
|
36
|
-
"eslint-config-silverwind": "
|
|
35
|
+
"@types/node": "25.9.2",
|
|
36
|
+
"@typescript/native-preview": "7.0.0-dev.20260609.1",
|
|
37
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
38
|
+
"eslint": "10.4.1",
|
|
39
|
+
"eslint-config-silverwind": "135.0.1",
|
|
37
40
|
"jest-extended": "7.0.0",
|
|
38
|
-
"tsdown": "0.22.
|
|
39
|
-
"tsdown-config-silverwind": "3.0.
|
|
41
|
+
"tsdown": "0.22.2",
|
|
42
|
+
"tsdown-config-silverwind": "3.0.3",
|
|
40
43
|
"typescript": "6.0.3",
|
|
41
|
-
"typescript-config-silverwind": "
|
|
42
|
-
"updates": "17.
|
|
43
|
-
"updates-config-silverwind": "
|
|
44
|
-
"versions": "15.
|
|
45
|
-
"vitest": "4.1.
|
|
46
|
-
"vitest-config-silverwind": "11.3.
|
|
44
|
+
"typescript-config-silverwind": "20.0.0",
|
|
45
|
+
"updates": "17.18.0",
|
|
46
|
+
"updates-config-silverwind": "4.0.0",
|
|
47
|
+
"versions": "15.1.1",
|
|
48
|
+
"vitest": "4.1.8",
|
|
49
|
+
"vitest-config-silverwind": "11.3.6"
|
|
47
50
|
}
|
|
48
51
|
}
|