cidr-tools 11.1.0 → 11.2.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.
Files changed (2) hide show
  1. package/dist/index.js +366 -131
  2. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -29,8 +29,23 @@ function parseIPv4Fast(s) {
29
29
  function formatIPv4Fast(n) {
30
30
  return `${n >>> 24 & 255}.${n >>> 16 & 255}.${n >>> 8 & 255}.${n & 255}`;
31
31
  }
32
+ function parsePrefixNum(str, slashIndex) {
33
+ if (slashIndex === -1) return -1;
34
+ if (slashIndex + 1 >= str.length) throw new Error(`Network is not a CIDR or IP: "${str}"`);
35
+ let prefixNum = 0;
36
+ for (let i = slashIndex + 1; i < str.length; i++) {
37
+ const c = str.charCodeAt(i);
38
+ if (c < 48 || c > 57) throw new Error(`Network is not a CIDR or IP: "${str}"`);
39
+ prefixNum = prefixNum * 10 + (c - 48);
40
+ }
41
+ return prefixNum;
42
+ }
32
43
  function doNormalize(cidr, { compress = true, hexify = false } = {}) {
33
44
  const { start, end, prefix, version, prefixPresent } = parseCidr(cidr);
45
+ if (version === 4) {
46
+ const ip = formatIPv4Fast(Number(start));
47
+ return start !== end || prefixPresent ? `${ip}/${prefix}` : ip;
48
+ }
34
49
  if (start !== end || prefixPresent) return `${normalizeIp(stringifyIp({
35
50
  number: start,
36
51
  version
@@ -52,28 +67,55 @@ function normalizeCidr(cidr, opts) {
52
67
  function parseCidr(str) {
53
68
  const slashIndex = str.indexOf("/");
54
69
  let ipPart;
55
- let prefix;
70
+ let prefixNum;
56
71
  let prefixPresent;
57
72
  if (slashIndex !== -1) {
58
73
  ipPart = str.substring(0, slashIndex);
59
- prefix = str.substring(slashIndex + 1);
60
- if (!/^[0-9]+$/.test(prefix)) throw new Error(`Network is not a CIDR or IP: "${str}"`);
74
+ prefixNum = parsePrefixNum(str, slashIndex);
61
75
  prefixPresent = true;
62
76
  } else {
63
77
  ipPart = str;
78
+ prefixNum = -1;
64
79
  prefixPresent = false;
65
- prefix = "";
80
+ }
81
+ if (!ipPart.includes(":")) {
82
+ const v4num = parseIPv4Fast(ipPart);
83
+ if (v4num !== -1) {
84
+ if (prefixNum === -1) prefixNum = 32;
85
+ const ip = formatIPv4Fast(v4num);
86
+ const prefix = String(prefixNum);
87
+ const hostBits = 32 - prefixNum;
88
+ let startNum, endNum;
89
+ if (hostBits >= 32) {
90
+ startNum = 0;
91
+ endNum = 4294967295;
92
+ } else {
93
+ const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
94
+ startNum = (v4num & ~mask) >>> 0;
95
+ endNum = (v4num | mask) >>> 0;
96
+ }
97
+ return {
98
+ cidr: `${ip}/${prefix}`,
99
+ ip,
100
+ version: 4,
101
+ prefix,
102
+ prefixPresent,
103
+ start: BigInt(startNum),
104
+ end: BigInt(endNum)
105
+ };
106
+ }
66
107
  }
67
108
  const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
68
109
  if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
69
- if (!prefixPresent) prefix = String(bits[version]);
110
+ if (prefixNum === -1) prefixNum = bits[version];
111
+ const prefix = String(prefixNum);
70
112
  const ip = stringifyIp({
71
113
  number,
72
114
  version,
73
115
  ipv4mapped,
74
116
  scopeid
75
117
  });
76
- const hostBits = bits[version] - Number(prefix);
118
+ const hostBits = bits[version] - prefixNum;
77
119
  const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
78
120
  return {
79
121
  cidr: `${ip}/${prefix}`,
@@ -91,13 +133,7 @@ function parseCidrLean(str) {
91
133
  let prefixNum;
92
134
  if (slashIndex !== -1) {
93
135
  ipPart = str.substring(0, slashIndex);
94
- if (slashIndex + 1 >= str.length) throw new Error(`Network is not a CIDR or IP: "${str}"`);
95
- prefixNum = 0;
96
- for (let i = slashIndex + 1; i < str.length; i++) {
97
- const c = str.charCodeAt(i);
98
- if (c < 48 || c > 57) throw new Error(`Network is not a CIDR or IP: "${str}"`);
99
- prefixNum = prefixNum * 10 + (c - 48);
100
- }
136
+ prefixNum = parsePrefixNum(str, slashIndex);
101
137
  } else {
102
138
  ipPart = str;
103
139
  prefixNum = -1;
@@ -108,14 +144,14 @@ function parseCidrLean(str) {
108
144
  if (prefixNum === -1) prefixNum = 32;
109
145
  const hostBits = 32 - prefixNum;
110
146
  if (hostBits >= 32) return {
111
- start: 0n,
112
- end: 4294967295n,
147
+ start: 0,
148
+ end: 4294967295,
113
149
  version: 4
114
150
  };
115
151
  const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
116
152
  return {
117
- start: BigInt((v4num & ~mask) >>> 0),
118
- end: BigInt((v4num | mask) >>> 0),
153
+ start: (v4num & ~mask) >>> 0,
154
+ end: (v4num | mask) >>> 0,
119
155
  version: 4
120
156
  };
121
157
  }
@@ -124,18 +160,114 @@ function parseCidrLean(str) {
124
160
  if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
125
161
  if (prefixNum === -1) prefixNum = bits[version];
126
162
  const hostBits = bits[version] - prefixNum;
163
+ if (version === 4) {
164
+ const num = Number(number);
165
+ if (hostBits >= 32) return {
166
+ start: 0,
167
+ end: 4294967295,
168
+ version: 4
169
+ };
170
+ const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
171
+ return {
172
+ start: (num & ~mask) >>> 0,
173
+ end: (num | mask) >>> 0,
174
+ version: 4
175
+ };
176
+ }
127
177
  const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
128
178
  return {
129
179
  start: number & ~mask,
130
180
  end: number | mask,
131
- version
181
+ version: 6
132
182
  };
133
183
  }
184
+ function bigintBitLength(n) {
185
+ if (n === 0n) return 0;
186
+ let len = 0;
187
+ if (n >= 18446744073709551616n) {
188
+ n >>= 64n;
189
+ len = 64;
190
+ }
191
+ while (n >= 4294967296n) {
192
+ n >>= 32n;
193
+ len += 32;
194
+ }
195
+ return len + 32 - Math.clz32(Number(n));
196
+ }
134
197
  function biggestPowerOfTwo(num) {
135
198
  if (num === 0n) return 0n;
136
- return 1n << BigInt(num.toString(2).length - 1);
199
+ return 1n << BigInt(bigintBitLength(num) - 1);
200
+ }
201
+ function biggestPowerOfTwo4(num) {
202
+ if (num === 0) return 0;
203
+ if (num >= 4294967296) return 4294967296;
204
+ return 1 << 31 - Math.clz32(num) >>> 0;
205
+ }
206
+ function subparts4(pStart, pEnd, output) {
207
+ if (pEnd < pStart) return;
208
+ if (pEnd === pStart) {
209
+ output.push({
210
+ start: pStart,
211
+ end: pEnd
212
+ });
213
+ return;
214
+ }
215
+ if (pEnd - pStart === 1) {
216
+ if (pEnd % 2 === 0) output.push({
217
+ start: pStart,
218
+ end: pStart
219
+ }, {
220
+ start: pEnd,
221
+ end: pEnd
222
+ });
223
+ else output.push({
224
+ start: pStart,
225
+ end: pEnd
226
+ });
227
+ return;
228
+ }
229
+ const size = pEnd - pStart + 1;
230
+ if ((size & size - 1) === 0 && pStart % size === 0) {
231
+ output.push({
232
+ start: pStart,
233
+ end: pEnd
234
+ });
235
+ return;
236
+ }
237
+ let biggest = biggestPowerOfTwo4(size);
238
+ let start;
239
+ let end;
240
+ if (size === biggest && pStart % biggest === 0) {
241
+ output.push({
242
+ start: pStart,
243
+ end: pEnd
244
+ });
245
+ return;
246
+ } else if (pStart % biggest === 0) {
247
+ start = pStart;
248
+ end = start + biggest - 1;
249
+ } else {
250
+ start = Math.floor(pEnd / biggest) * biggest;
251
+ if (start + biggest - 1 > pEnd) {
252
+ start = (Math.floor(pEnd / biggest) - 1) * biggest;
253
+ while (start < pStart) {
254
+ biggest /= 2;
255
+ start = (Math.floor(pEnd / biggest) - 1) * biggest;
256
+ }
257
+ end = start + biggest - 1;
258
+ } else {
259
+ start = Math.floor(pEnd / biggest) * biggest;
260
+ end = start + biggest - 1;
261
+ }
262
+ }
263
+ if (start !== pStart) subparts4(pStart, start - 1, output);
264
+ output.push({
265
+ start,
266
+ end
267
+ });
268
+ if (end !== pEnd) subparts4(end + 1, pEnd, output);
137
269
  }
138
- function subparts(pStart, pEnd, output) {
270
+ function subparts6(pStart, pEnd, output) {
139
271
  if (pEnd < pStart) return;
140
272
  if (pEnd === pStart) {
141
273
  output.push({
@@ -158,58 +290,86 @@ function subparts(pStart, pEnd, output) {
158
290
  });
159
291
  return;
160
292
  }
161
- const size = diff(pEnd, pStart);
293
+ const size = pEnd - pStart + 1n;
294
+ if ((size & size - 1n) === 0n && (pStart & size - 1n) === 0n) {
295
+ output.push({
296
+ start: pStart,
297
+ end: pEnd
298
+ });
299
+ return;
300
+ }
162
301
  let biggest = biggestPowerOfTwo(size);
163
302
  let start;
164
303
  let end;
165
- if (size === biggest && pStart % biggest === 0n) {
304
+ if (size === biggest && (pStart & biggest - 1n) === 0n) {
166
305
  output.push({
167
306
  start: pStart,
168
307
  end: pEnd
169
308
  });
170
309
  return;
171
- } else if (pStart % biggest === 0n) {
310
+ } else if ((pStart & biggest - 1n) === 0n) {
172
311
  start = pStart;
173
312
  end = start + biggest - 1n;
174
313
  } else {
175
- start = pEnd / biggest * biggest;
314
+ start = pEnd & -biggest;
176
315
  if (start + biggest - 1n > pEnd) {
177
- start = (pEnd / biggest - 1n) * biggest;
316
+ start = (pEnd & -biggest) - biggest;
178
317
  while (start < pStart) {
179
- biggest /= 2n;
180
- start = (pEnd / biggest - 1n) * biggest;
318
+ biggest >>= 1n;
319
+ start = (pEnd & -biggest) - biggest;
181
320
  }
182
321
  end = start + biggest - 1n;
183
322
  } else {
184
- start = pEnd / biggest * biggest;
323
+ start = pEnd & -biggest;
185
324
  end = start + biggest - 1n;
186
325
  }
187
326
  }
188
- if (start !== pStart) subparts(pStart, start - 1n, output);
327
+ if (start !== pStart) subparts6(pStart, start - 1n, output);
189
328
  output.push({
190
329
  start,
191
330
  end
192
331
  });
193
- if (end !== pEnd) subparts(end + 1n, pEnd, output);
332
+ if (end !== pEnd) subparts6(end + 1n, pEnd, output);
194
333
  }
195
- function diff(a, b) {
196
- return a + 1n - b;
334
+ function formatPart4(part) {
335
+ const ip = formatIPv4Fast(part.start);
336
+ const size = part.end - part.start + 1;
337
+ return `${ip}/${32 - (size <= 1 ? 0 : size >= 4294967296 ? 32 : 31 - Math.clz32(size))}`;
197
338
  }
198
- function formatPart(part, version) {
199
- if (version === 4) {
200
- const ip = formatIPv4Fast(Number(part.start));
201
- const sizeNum = Number(part.end - part.start) + 1;
202
- return `${ip}/${32 - (sizeNum <= 1 ? 0 : sizeNum >= 4294967296 ? 32 : 31 - Math.clz32(sizeNum))}`;
203
- }
339
+ function formatPart6(part) {
204
340
  const ip = stringifyIp({
205
341
  number: part.start,
206
- version
342
+ version: 6
207
343
  });
208
- const size = diff(part.end, part.start);
209
- const hostBits = size <= 1n ? 0 : size.toString(2).length - 1;
210
- return `${ip}/${bits[version] - hostBits}`;
344
+ const size = part.end - part.start + 1n;
345
+ return `${ip}/${128 - (size <= 1n ? 0 : bigintBitLength(size) - 1)}`;
211
346
  }
212
- function mergeIntervalsRaw(nets) {
347
+ function mergeIntervalsRaw4(nets) {
348
+ if (nets.length === 0) return [];
349
+ nets.sort((a, b) => a.start - b.start || a.end - b.end);
350
+ const merged = [];
351
+ let curStart = nets[0].start;
352
+ let curEnd = nets[0].end;
353
+ for (let i = 1; i < nets.length; i++) {
354
+ const { start, end } = nets[i];
355
+ if (start <= curEnd + 1) {
356
+ if (end > curEnd) curEnd = end;
357
+ } else {
358
+ merged.push({
359
+ start: curStart,
360
+ end: curEnd
361
+ });
362
+ curStart = start;
363
+ curEnd = end;
364
+ }
365
+ }
366
+ merged.push({
367
+ start: curStart,
368
+ end: curEnd
369
+ });
370
+ return merged;
371
+ }
372
+ function mergeIntervalsRaw6(nets) {
213
373
  if (nets.length === 0) return [];
214
374
  nets.sort((a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : a.end > b.end ? 1 : a.end < b.end ? -1 : 0);
215
375
  const merged = [];
@@ -234,12 +394,42 @@ function mergeIntervalsRaw(nets) {
234
394
  });
235
395
  return merged;
236
396
  }
237
- function mergeIntervals(nets) {
397
+ function mergeIntervals4(nets) {
238
398
  const merged = [];
239
- for (const part of mergeIntervalsRaw(nets)) subparts(part.start, part.end, merged);
399
+ for (const part of mergeIntervalsRaw4(nets)) subparts4(part.start, part.end, merged);
240
400
  return merged;
241
401
  }
242
- function subtractSorted(bases, excls) {
402
+ function mergeIntervals6(nets) {
403
+ const merged = [];
404
+ for (const part of mergeIntervalsRaw6(nets)) subparts6(part.start, part.end, merged);
405
+ return merged;
406
+ }
407
+ function subtractSorted4(bases, excls) {
408
+ if (excls.length === 0) return bases;
409
+ if (bases.length === 0) return [];
410
+ const result = [];
411
+ let j = 0;
412
+ for (const base of bases) {
413
+ let start = base.start;
414
+ const end = base.end;
415
+ while (j < excls.length && excls[j].end < start) j++;
416
+ let k = j;
417
+ while (k < excls.length && excls[k].start <= end && start <= end) {
418
+ if (excls[k].start > start) result.push({
419
+ start,
420
+ end: excls[k].start - 1
421
+ });
422
+ start = excls[k].end + 1;
423
+ k++;
424
+ }
425
+ if (start <= end) result.push({
426
+ start,
427
+ end
428
+ });
429
+ }
430
+ return result;
431
+ }
432
+ function subtractSorted6(bases, excls) {
243
433
  if (excls.length === 0) return bases;
244
434
  if (bases.length === 0) return [];
245
435
  const result = [];
@@ -266,83 +456,104 @@ function subtractSorted(bases, excls) {
266
456
  }
267
457
  /** Returns an array of merged networks */
268
458
  function mergeCidr(nets) {
269
- const arr = (Array.isArray(nets) ? nets : [nets]).map(parseCidrLean);
270
- const byVersion = {
271
- 4: [],
272
- 6: []
273
- };
274
- for (const n of arr) byVersion[n.version].push(n);
459
+ const arr = Array.isArray(nets) ? nets : [nets];
460
+ const v4 = [];
461
+ const v6 = [];
462
+ for (const s of arr) {
463
+ const n = parseCidrLean(s);
464
+ if (n.version === 4) v4.push(n);
465
+ else v6.push(n);
466
+ }
275
467
  const merged = [];
276
- for (const v of [4, 6]) for (const part of mergeIntervals(byVersion[v])) merged.push(formatPart(part, v));
468
+ for (const part of mergeIntervals4(v4)) merged.push(formatPart4(part));
469
+ for (const part of mergeIntervals6(v6)) merged.push(formatPart6(part));
277
470
  return merged;
278
471
  }
279
472
  /** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
280
473
  function excludeCidr(base, excl) {
281
- const baseArr = (Array.isArray(base) ? base : [base]).map(parseCidrLean);
282
- const exclArr = (Array.isArray(excl) ? excl : [excl]).map(parseCidrLean);
283
- const baseByVersion = {
284
- 4: [],
285
- 6: []
286
- };
287
- const exclByVersion = {
288
- 4: [],
289
- 6: []
290
- };
291
- for (const n of baseArr) baseByVersion[n.version].push(n);
292
- for (const n of exclArr) exclByVersion[n.version].push(n);
474
+ const baseArr = Array.isArray(base) ? base : [base];
475
+ const exclArr = Array.isArray(excl) ? excl : [excl];
476
+ const v4base = [], v6base = [];
477
+ const v4excl = [], v6excl = [];
478
+ for (const s of baseArr) {
479
+ const n = parseCidrLean(s);
480
+ if (n.version === 4) v4base.push(n);
481
+ else v6base.push(n);
482
+ }
483
+ for (const s of exclArr) {
484
+ const n = parseCidrLean(s);
485
+ if (n.version === 4) v4excl.push(n);
486
+ else v6excl.push(n);
487
+ }
293
488
  const result = [];
294
- for (const v of [4, 6]) {
295
- const remaining = subtractSorted(mergeIntervalsRaw(baseByVersion[v]), mergeIntervalsRaw(exclByVersion[v]));
489
+ {
490
+ const remaining = subtractSorted4(mergeIntervalsRaw4(v4base), mergeIntervalsRaw4(v4excl));
296
491
  const aligned = [];
297
- for (const part of remaining) subparts(part.start, part.end, aligned);
298
- for (const p of aligned) result.push(formatPart(p, v));
492
+ for (const part of remaining) subparts4(part.start, part.end, aligned);
493
+ for (const p of aligned) result.push(formatPart4(p));
494
+ }
495
+ {
496
+ const remaining = subtractSorted6(mergeIntervalsRaw6(v6base), mergeIntervalsRaw6(v6excl));
497
+ const aligned = [];
498
+ for (const part of remaining) subparts6(part.start, part.end, aligned);
499
+ for (const p of aligned) result.push(formatPart6(p));
299
500
  }
300
501
  return result;
301
502
  }
302
503
  function* expandCidr(nets) {
303
- const parsed = (Array.isArray(nets) ? nets : [nets]).map(parseCidrLean);
304
- const byVersion = {
305
- 4: [],
306
- 6: []
307
- };
308
- for (const n of parsed) byVersion[n.version].push(n);
309
- for (const v of [4, 6]) {
310
- if (byVersion[v].length === 0) continue;
311
- const intervals = mergeIntervalsRaw(byVersion[v]);
312
- if (v === 4) for (const part of intervals) {
313
- const startNum = Number(part.start);
314
- const endNum = Number(part.end);
315
- for (let n = startNum; n <= endNum; n++) yield formatIPv4Fast(n);
316
- }
317
- else for (const part of intervals) for (let num = part.start; num <= part.end; num++) yield stringifyIp({
318
- number: num,
319
- version: 6
320
- });
504
+ const arr = Array.isArray(nets) ? nets : [nets];
505
+ const v4 = [];
506
+ const v6 = [];
507
+ for (const s of arr) {
508
+ const n = parseCidrLean(s);
509
+ if (n.version === 4) v4.push(n);
510
+ else v6.push(n);
321
511
  }
512
+ if (v4.length > 0) for (const part of mergeIntervalsRaw4(v4)) for (let n = part.start; n <= part.end; n++) yield formatIPv4Fast(n);
513
+ if (v6.length > 0) for (const part of mergeIntervalsRaw6(v6)) for (let num = part.start; num <= part.end; num++) yield stringifyIp({
514
+ number: num,
515
+ version: 6
516
+ });
322
517
  }
323
518
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
324
519
  function overlapCidr(a, b) {
325
- const aNets = (Array.isArray(a) ? a : [a]).map(parseCidrLean);
326
- const bNets = (Array.isArray(b) ? b : [b]).map(parseCidrLean);
327
- const aByVersion = {
328
- 4: [],
329
- 6: []
330
- };
331
- const bByVersion = {
332
- 4: [],
333
- 6: []
334
- };
335
- for (const n of aNets) aByVersion[n.version].push(n);
336
- for (const n of bNets) bByVersion[n.version].push(n);
337
- for (const v of [4, 6]) {
338
- const aVer = aByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
339
- const bVer = bByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
520
+ if (!Array.isArray(a) && !Array.isArray(b)) {
521
+ const pa = parseCidrLean(a);
522
+ const pb = parseCidrLean(b);
523
+ if (pa.version !== pb.version) return false;
524
+ return pa.start <= pb.end && pb.start <= pa.end;
525
+ }
526
+ const aArr = Array.isArray(a) ? a : [a];
527
+ const bArr = Array.isArray(b) ? b : [b];
528
+ const v4a = [], v6a = [];
529
+ const v4b = [], v6b = [];
530
+ for (const s of aArr) {
531
+ const n = parseCidrLean(s);
532
+ if (n.version === 4) v4a.push(n);
533
+ else v6a.push(n);
534
+ }
535
+ for (const s of bArr) {
536
+ const n = parseCidrLean(s);
537
+ if (n.version === 4) v4b.push(n);
538
+ else v6b.push(n);
539
+ }
540
+ if (v4a.length > 0 && v4b.length > 0) {
541
+ v4a.sort((x, y) => x.start - y.start);
542
+ v4b.sort((x, y) => x.start - y.start);
543
+ let i = 0, j = 0;
544
+ while (i < v4a.length && j < v4b.length) {
545
+ if (v4a[i].start <= v4b[j].end && v4b[j].start <= v4a[i].end) return true;
546
+ if (v4a[i].end < v4b[j].end) i++;
547
+ else j++;
548
+ }
549
+ }
550
+ if (v6a.length > 0 && v6b.length > 0) {
551
+ v6a.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
552
+ v6b.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
340
553
  let i = 0, j = 0;
341
- while (i < aVer.length && j < bVer.length) {
342
- const aNet = aVer[i];
343
- const bNet = bVer[j];
344
- if (aNet.start <= bNet.end && bNet.start <= aNet.end) return true;
345
- if (aNet.end < bNet.end) i++;
554
+ while (i < v6a.length && j < v6b.length) {
555
+ if (v6a[i].start <= v6b[j].end && v6b[j].start <= v6a[i].end) return true;
556
+ if (v6a[i].end < v6b[j].end) i++;
346
557
  else j++;
347
558
  }
348
559
  }
@@ -350,33 +561,57 @@ function overlapCidr(a, b) {
350
561
  }
351
562
  /** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
352
563
  function containsCidr(a, b) {
353
- const aNets = (Array.isArray(a) ? a : [a]).map(parseCidrLean);
354
- const bNets = (Array.isArray(b) ? b : [b]).map(parseCidrLean);
355
- const aByVersion = {
356
- 4: [],
357
- 6: []
358
- };
359
- const bByVersion = {
360
- 4: [],
361
- 6: []
362
- };
363
- for (const n of aNets) aByVersion[n.version].push(n);
364
- for (const n of bNets) bByVersion[n.version].push(n);
365
- for (const v of [4, 6]) {
366
- const containers = aByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
367
- const targets = bByVersion[v];
368
- if (targets.length === 0) continue;
369
- if (containers.length === 0) return false;
370
- const maxEnd = new Array(containers.length);
371
- maxEnd[0] = containers[0].end;
372
- for (let i = 1; i < containers.length; i++) if (containers[i].end > maxEnd[i - 1]) maxEnd[i] = containers[i].end;
373
- else maxEnd[i] = maxEnd[i - 1];
374
- for (const target of targets) {
375
- let lo = 0, hi = containers.length - 1;
564
+ if (!Array.isArray(a) && !Array.isArray(b)) {
565
+ const pa = parseCidrLean(a);
566
+ const pb = parseCidrLean(b);
567
+ if (pa.version !== pb.version) return false;
568
+ return pa.start <= pb.start && pa.end >= pb.end;
569
+ }
570
+ const aArr = Array.isArray(a) ? a : [a];
571
+ const bArr = Array.isArray(b) ? b : [b];
572
+ const v4a = [], v6a = [];
573
+ const v4b = [], v6b = [];
574
+ for (const s of aArr) {
575
+ const n = parseCidrLean(s);
576
+ if (n.version === 4) v4a.push(n);
577
+ else v6a.push(n);
578
+ }
579
+ for (const s of bArr) {
580
+ const n = parseCidrLean(s);
581
+ if (n.version === 4) v4b.push(n);
582
+ else v6b.push(n);
583
+ }
584
+ if (v4b.length > 0) {
585
+ if (v4a.length === 0) return false;
586
+ v4a.sort((x, y) => x.start - y.start);
587
+ const maxEnd = new Array(v4a.length);
588
+ maxEnd[0] = v4a[0].end;
589
+ for (let i = 1; i < v4a.length; i++) maxEnd[i] = Math.max(v4a[i].end, maxEnd[i - 1]);
590
+ for (const target of v4b) {
591
+ let lo = 0, hi = v4a.length - 1;
592
+ let idx = -1;
593
+ while (lo <= hi) {
594
+ const mid = lo + hi >> 1;
595
+ if (v4a[mid].start <= target.start) {
596
+ idx = mid;
597
+ lo = mid + 1;
598
+ } else hi = mid - 1;
599
+ }
600
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
601
+ }
602
+ }
603
+ if (v6b.length > 0) {
604
+ if (v6a.length === 0) return false;
605
+ v6a.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
606
+ const maxEnd = new Array(v6a.length);
607
+ maxEnd[0] = v6a[0].end;
608
+ for (let i = 1; i < v6a.length; i++) maxEnd[i] = v6a[i].end > maxEnd[i - 1] ? v6a[i].end : maxEnd[i - 1];
609
+ for (const target of v6b) {
610
+ let lo = 0, hi = v6a.length - 1;
376
611
  let idx = -1;
377
612
  while (lo <= hi) {
378
613
  const mid = lo + hi >> 1;
379
- if (containers[mid].start <= target.start) {
614
+ if (v6a[mid].start <= target.start) {
380
615
  idx = mid;
381
616
  lo = mid + 1;
382
617
  } else hi = mid - 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "11.1.0",
3
+ "version": "11.2.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",
@@ -17,19 +17,19 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.2.9"
20
+ "ip-bigint": "^8.2.12"
21
21
  },
22
22
  "devDependencies": {
23
- "@types/node": "25.3.3",
24
- "@typescript/native-preview": "7.0.0-dev.20260305.1",
25
- "eslint": "9.39.3",
26
- "eslint-config-silverwind": "121.2.0",
23
+ "@types/node": "25.4.0",
24
+ "@typescript/native-preview": "7.0.0-dev.20260311.1",
25
+ "eslint": "9.39.4",
26
+ "eslint-config-silverwind": "124.0.7",
27
27
  "jest-extended": "7.0.0",
28
- "tsdown": "0.21.0",
29
- "tsdown-config-silverwind": "2.0.0",
28
+ "tsdown": "0.21.2",
29
+ "tsdown-config-silverwind": "2.0.1",
30
30
  "typescript": "5.9.3",
31
31
  "typescript-config-silverwind": "15.0.0",
32
- "updates": "17.8.1",
32
+ "updates": "17.9.0",
33
33
  "updates-config-silverwind": "1.0.3",
34
34
  "versions": "14.2.1",
35
35
  "vitest": "4.0.18",