cidr-tools 12.0.2 → 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.js +81 -64
- package/package.json +19 -16
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,23 +77,33 @@ 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) {
|
|
@@ -129,7 +143,8 @@ function parseCidr(str) {
|
|
|
129
143
|
let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
|
|
130
144
|
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
|
|
131
145
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
132
|
-
|
|
146
|
+
const numBits = bits[version];
|
|
147
|
+
if (prefixNum === -1) prefixNum = numBits;
|
|
133
148
|
const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
|
|
134
149
|
const ip = stringifyIp({
|
|
135
150
|
number,
|
|
@@ -137,16 +152,21 @@ function parseCidr(str) {
|
|
|
137
152
|
ipv4mapped,
|
|
138
153
|
scopeid
|
|
139
154
|
});
|
|
140
|
-
const hostBits =
|
|
141
|
-
|
|
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
|
+
}
|
|
142
162
|
return {
|
|
143
163
|
cidr: ip + prefixStrings[prefixNum],
|
|
144
164
|
ip,
|
|
145
165
|
version,
|
|
146
166
|
prefix,
|
|
147
167
|
prefixPresent,
|
|
148
|
-
start
|
|
149
|
-
end
|
|
168
|
+
start,
|
|
169
|
+
end
|
|
150
170
|
};
|
|
151
171
|
}
|
|
152
172
|
function parseCidrLean(str) {
|
|
@@ -160,8 +180,9 @@ function parseCidrLean(str) {
|
|
|
160
180
|
let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
|
|
161
181
|
const { number, version } = parseIp(ipPart);
|
|
162
182
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
163
|
-
|
|
164
|
-
|
|
183
|
+
const numBits = bits[version];
|
|
184
|
+
if (prefixNum === -1) prefixNum = numBits;
|
|
185
|
+
const hostBits = numBits - prefixNum;
|
|
165
186
|
if (version === 4) {
|
|
166
187
|
const num = Number(number);
|
|
167
188
|
if (hostBits >= 32) return {
|
|
@@ -176,10 +197,14 @@ function parseCidrLean(str) {
|
|
|
176
197
|
version: 4
|
|
177
198
|
};
|
|
178
199
|
}
|
|
179
|
-
|
|
200
|
+
if (hostBits <= 0) return {
|
|
201
|
+
start: number,
|
|
202
|
+
end: number,
|
|
203
|
+
version: 6
|
|
204
|
+
};
|
|
180
205
|
return {
|
|
181
|
-
start: number &
|
|
182
|
-
end: number |
|
|
206
|
+
start: number & hostNotMasks[hostBits],
|
|
207
|
+
end: number | hostMasks[hostBits],
|
|
183
208
|
version: 6
|
|
184
209
|
};
|
|
185
210
|
}
|
|
@@ -211,10 +236,7 @@ function subparts4(pStart, pEnd, output) {
|
|
|
211
236
|
const size = pEnd - start + 1;
|
|
212
237
|
const lowBit = (start & -start) >>> 0;
|
|
213
238
|
const blockSize = lowBit !== 0 && lowBit <= size ? lowBit : biggestPowerOfTwo4(size);
|
|
214
|
-
output.push(
|
|
215
|
-
start,
|
|
216
|
-
end: start + blockSize - 1
|
|
217
|
-
});
|
|
239
|
+
output.push(formatIPv4Fast(start) + prefixStrings[Math.clz32(blockSize - 1)]);
|
|
218
240
|
start += blockSize;
|
|
219
241
|
}
|
|
220
242
|
}
|
|
@@ -224,29 +246,20 @@ function subparts6(pStart, pEnd, output) {
|
|
|
224
246
|
const size = pEnd - start + 1n;
|
|
225
247
|
const lowBit = start & -start;
|
|
226
248
|
if ((size & size - 1n) === 0n && (lowBit === 0n || lowBit >= size)) {
|
|
227
|
-
output.push({
|
|
228
|
-
start,
|
|
229
|
-
|
|
230
|
-
});
|
|
249
|
+
output.push(stringifyIp({
|
|
250
|
+
number: start,
|
|
251
|
+
version: 6
|
|
252
|
+
}) + prefixStrings[129 - bigintBitLength(size)]);
|
|
231
253
|
return;
|
|
232
254
|
}
|
|
233
255
|
const blockSize = lowBit !== 0n && lowBit <= size ? lowBit : biggestPowerOfTwo(size);
|
|
234
|
-
output.push({
|
|
235
|
-
start,
|
|
236
|
-
|
|
237
|
-
});
|
|
256
|
+
output.push(stringifyIp({
|
|
257
|
+
number: start,
|
|
258
|
+
version: 6
|
|
259
|
+
}) + prefixStrings[129 - bigintBitLength(blockSize)]);
|
|
238
260
|
start += blockSize;
|
|
239
261
|
}
|
|
240
262
|
}
|
|
241
|
-
function formatPart4(part) {
|
|
242
|
-
return formatIPv4Fast(part.start) + prefixStrings[Math.clz32(part.end - part.start)];
|
|
243
|
-
}
|
|
244
|
-
function formatPart6(part) {
|
|
245
|
-
return stringifyIp({
|
|
246
|
-
number: part.start,
|
|
247
|
-
version: 6
|
|
248
|
-
}) + prefixStrings[128 - bigintBitLength(part.end - part.start)];
|
|
249
|
-
}
|
|
250
263
|
function mergeIntervalsRaw4(nets) {
|
|
251
264
|
if (nets.length === 0) return [];
|
|
252
265
|
nets.sort(cmpV4StartEnd);
|
|
@@ -297,16 +310,6 @@ function mergeIntervalsRaw6(nets) {
|
|
|
297
310
|
});
|
|
298
311
|
return merged;
|
|
299
312
|
}
|
|
300
|
-
function mergeIntervals4(nets) {
|
|
301
|
-
const merged = [];
|
|
302
|
-
for (const part of mergeIntervalsRaw4(nets)) subparts4(part.start, part.end, merged);
|
|
303
|
-
return merged;
|
|
304
|
-
}
|
|
305
|
-
function mergeIntervals6(nets) {
|
|
306
|
-
const merged = [];
|
|
307
|
-
for (const part of mergeIntervalsRaw6(nets)) subparts6(part.start, part.end, merged);
|
|
308
|
-
return merged;
|
|
309
|
-
}
|
|
310
313
|
function subtractSorted4(bases, excls) {
|
|
311
314
|
if (excls.length === 0) return bases;
|
|
312
315
|
if (bases.length === 0) return [];
|
|
@@ -368,8 +371,8 @@ function mergeCidr(nets) {
|
|
|
368
371
|
else v6.push(n);
|
|
369
372
|
}
|
|
370
373
|
const merged = [];
|
|
371
|
-
for (const part of
|
|
372
|
-
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);
|
|
373
376
|
return merged;
|
|
374
377
|
}
|
|
375
378
|
/** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
|
|
@@ -390,16 +393,14 @@ function excludeCidr(base, excl) {
|
|
|
390
393
|
}
|
|
391
394
|
const result = [];
|
|
392
395
|
{
|
|
393
|
-
const
|
|
394
|
-
const
|
|
395
|
-
for (const part of
|
|
396
|
-
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);
|
|
397
399
|
}
|
|
398
400
|
{
|
|
399
|
-
const
|
|
400
|
-
const
|
|
401
|
-
for (const part of
|
|
402
|
-
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);
|
|
403
404
|
}
|
|
404
405
|
return result;
|
|
405
406
|
}
|
|
@@ -430,9 +431,25 @@ function* expandCidr(nets) {
|
|
|
430
431
|
number: 0n,
|
|
431
432
|
version: 6
|
|
432
433
|
};
|
|
433
|
-
for (const part of mergeIntervalsRaw6(v6))
|
|
434
|
-
|
|
435
|
-
|
|
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
|
+
}
|
|
436
453
|
}
|
|
437
454
|
}
|
|
438
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.9.
|
|
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.1.
|
|
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
|
}
|