cidr-tools 11.3.1 → 11.3.2

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 +86 -48
  2. package/package.json +11 -11
package/dist/index.js CHANGED
@@ -6,8 +6,13 @@ const bits = {
6
6
  6: 128
7
7
  };
8
8
  const octetStrings = Array.from({ length: 256 }, (_, i) => String(i));
9
+ const octetDotStrings = Array.from({ length: 256 }, (_, i) => `${i}.`);
9
10
  const prefixStrings = Array.from({ length: 129 }, (_, i) => `/${i}`);
10
11
  const prefixNumStrings = Array.from({ length: 129 }, (_, i) => String(i));
12
+ const cmpV4StartEnd = (a, b) => a.start - b.start || a.end - b.end;
13
+ const cmpV4Start = (a, b) => a.start - b.start;
14
+ 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;
15
+ const cmpV6Start = (a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : 0;
11
16
  function parseIPv4Fast(s, end) {
12
17
  let num = 0;
13
18
  let octet = 0;
@@ -17,7 +22,7 @@ function parseIPv4Fast(s, end) {
17
22
  const c = s.charCodeAt(i);
18
23
  if (c === 46) {
19
24
  if (digits === 0 || octet > 255) return -1;
20
- num = (num << 8 | octet) >>> 0;
25
+ num = num << 8 | octet;
21
26
  octet = 0;
22
27
  dots++;
23
28
  digits = 0;
@@ -30,7 +35,7 @@ function parseIPv4Fast(s, end) {
30
35
  return (num << 8 | octet) >>> 0;
31
36
  }
32
37
  function formatIPv4Fast(n) {
33
- return `${octetStrings[n >>> 24 & 255]}.${octetStrings[n >>> 16 & 255]}.${octetStrings[n >>> 8 & 255]}.${octetStrings[n & 255]}`;
38
+ return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
34
39
  }
35
40
  function parsePrefixNum(str, slashIndex) {
36
41
  if (slashIndex === -1) return -1;
@@ -330,21 +335,17 @@ function subparts6(pStart, pEnd, output) {
330
335
  if (end !== pEnd) subparts6(end + 1n, pEnd, output);
331
336
  }
332
337
  function formatPart4(part) {
333
- const ip = formatIPv4Fast(part.start);
334
- const size = part.end - part.start + 1;
335
- return ip + prefixStrings[32 - (size <= 1 ? 0 : size >= 4294967296 ? 32 : 31 - Math.clz32(size))];
338
+ return formatIPv4Fast(part.start) + prefixStrings[Math.clz32(part.end - part.start)];
336
339
  }
337
340
  function formatPart6(part) {
338
- const ip = stringifyIp({
341
+ return stringifyIp({
339
342
  number: part.start,
340
343
  version: 6
341
- });
342
- const size = part.end - part.start + 1n;
343
- return ip + prefixStrings[128 - (size <= 1n ? 0 : bigintBitLength(size) - 1)];
344
+ }) + prefixStrings[128 - bigintBitLength(part.end - part.start)];
344
345
  }
345
346
  function mergeIntervalsRaw4(nets) {
346
347
  if (nets.length === 0) return [];
347
- nets.sort((a, b) => a.start - b.start || a.end - b.end);
348
+ nets.sort(cmpV4StartEnd);
348
349
  const merged = [];
349
350
  let curStart = nets[0].start;
350
351
  let curEnd = nets[0].end;
@@ -369,7 +370,7 @@ function mergeIntervalsRaw4(nets) {
369
370
  }
370
371
  function mergeIntervalsRaw6(nets) {
371
372
  if (nets.length === 0) return [];
372
- 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);
373
+ nets.sort(cmpV6StartEnd);
373
374
  const merged = [];
374
375
  let curStart = nets[0].start;
375
376
  let curEnd = nets[0].end;
@@ -507,11 +508,28 @@ function* expandCidr(nets) {
507
508
  if (n.version === 4) v4.push(n);
508
509
  else v6.push(n);
509
510
  }
510
- if (v4.length > 0) for (const part of mergeIntervalsRaw4(v4)) for (let n = part.start; n <= part.end; n++) yield formatIPv4Fast(n);
511
- if (v6.length > 0) for (const part of mergeIntervalsRaw6(v6)) for (let num = part.start; num <= part.end; num++) yield stringifyIp({
512
- number: num,
513
- version: 6
514
- });
511
+ if (v4.length > 0) for (const part of mergeIntervalsRaw4(v4)) {
512
+ let prevHigh = -1;
513
+ let prefix = "";
514
+ for (let n = part.start; n <= part.end; n++) {
515
+ const high = n >>> 8;
516
+ if (high !== prevHigh) {
517
+ prefix = octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255];
518
+ prevHigh = high;
519
+ }
520
+ yield prefix + octetStrings[n & 255];
521
+ }
522
+ }
523
+ if (v6.length > 0) {
524
+ const ipObj = {
525
+ number: 0n,
526
+ version: 6
527
+ };
528
+ for (const part of mergeIntervalsRaw6(v6)) for (let num = part.start; num <= part.end; num++) {
529
+ ipObj.number = num;
530
+ yield stringifyIp(ipObj);
531
+ }
532
+ }
515
533
  }
516
534
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
517
535
  function overlapCidr(a, b) {
@@ -546,8 +564,8 @@ function overlapCidr(a, b) {
546
564
  const as = v4a[0].start, ae = v4a[0].end;
547
565
  for (const el of v4b) if (as <= el.end && el.start <= ae) return true;
548
566
  } else {
549
- v4a.sort((x, y) => x.start - y.start);
550
- v4b.sort((x, y) => x.start - y.start);
567
+ v4a.sort(cmpV4Start);
568
+ v4b.sort(cmpV4Start);
551
569
  let i = 0, j = 0;
552
570
  while (i < v4a.length && j < v4b.length) {
553
571
  if (v4a[i].start <= v4b[j].end && v4b[j].start <= v4a[i].end) return true;
@@ -562,8 +580,8 @@ function overlapCidr(a, b) {
562
580
  const as = v6a[0].start, ae = v6a[0].end;
563
581
  for (const el of v6b) if (as <= el.end && el.start <= ae) return true;
564
582
  } else {
565
- v6a.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
566
- v6b.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
583
+ v6a.sort(cmpV6Start);
584
+ v6b.sort(cmpV6Start);
567
585
  let i = 0, j = 0;
568
586
  while (i < v6a.length && j < v6b.length) {
569
587
  if (v6a[i].start <= v6b[j].end && v6b[j].start <= v6a[i].end) return true;
@@ -601,40 +619,60 @@ function containsCidr(a, b) {
601
619
  }
602
620
  if (v4b.length > 0) {
603
621
  if (v4a.length === 0) return false;
604
- v4a.sort((x, y) => x.start - y.start);
605
- const maxEnd = new Array(v4a.length);
606
- maxEnd[0] = v4a[0].end;
607
- for (let i = 1; i < v4a.length; i++) maxEnd[i] = Math.max(v4a[i].end, maxEnd[i - 1]);
608
- for (const target of v4b) {
609
- let lo = 0, hi = v4a.length - 1;
610
- let idx = -1;
611
- while (lo <= hi) {
612
- const mid = lo + hi >> 1;
613
- if (v4a[mid].start <= target.start) {
614
- idx = mid;
615
- lo = mid + 1;
616
- } else hi = mid - 1;
622
+ if (v4b.length === 1) {
623
+ const ts = v4b[0].start, te = v4b[0].end;
624
+ let found = false;
625
+ for (const a of v4a) if (a.start <= ts && a.end >= te) {
626
+ found = true;
627
+ break;
628
+ }
629
+ if (!found) return false;
630
+ } else {
631
+ v4a.sort(cmpV4Start);
632
+ const maxEnd = new Array(v4a.length);
633
+ maxEnd[0] = v4a[0].end;
634
+ for (let i = 1; i < v4a.length; i++) maxEnd[i] = Math.max(v4a[i].end, maxEnd[i - 1]);
635
+ for (const target of v4b) {
636
+ let lo = 0, hi = v4a.length - 1;
637
+ let idx = -1;
638
+ while (lo <= hi) {
639
+ const mid = lo + hi >> 1;
640
+ if (v4a[mid].start <= target.start) {
641
+ idx = mid;
642
+ lo = mid + 1;
643
+ } else hi = mid - 1;
644
+ }
645
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
617
646
  }
618
- if (idx < 0 || maxEnd[idx] < target.end) return false;
619
647
  }
620
648
  }
621
649
  if (v6b.length > 0) {
622
650
  if (v6a.length === 0) return false;
623
- v6a.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
624
- const maxEnd = new Array(v6a.length);
625
- maxEnd[0] = v6a[0].end;
626
- for (let i = 1; i < v6a.length; i++) maxEnd[i] = v6a[i].end > maxEnd[i - 1] ? v6a[i].end : maxEnd[i - 1];
627
- for (const target of v6b) {
628
- let lo = 0, hi = v6a.length - 1;
629
- let idx = -1;
630
- while (lo <= hi) {
631
- const mid = lo + hi >> 1;
632
- if (v6a[mid].start <= target.start) {
633
- idx = mid;
634
- lo = mid + 1;
635
- } else hi = mid - 1;
651
+ if (v6b.length === 1) {
652
+ const ts = v6b[0].start, te = v6b[0].end;
653
+ let found = false;
654
+ for (const a of v6a) if (a.start <= ts && a.end >= te) {
655
+ found = true;
656
+ break;
657
+ }
658
+ if (!found) return false;
659
+ } else {
660
+ v6a.sort(cmpV6Start);
661
+ const maxEnd = new Array(v6a.length);
662
+ maxEnd[0] = v6a[0].end;
663
+ for (let i = 1; i < v6a.length; i++) maxEnd[i] = v6a[i].end > maxEnd[i - 1] ? v6a[i].end : maxEnd[i - 1];
664
+ for (const target of v6b) {
665
+ let lo = 0, hi = v6a.length - 1;
666
+ let idx = -1;
667
+ while (lo <= hi) {
668
+ const mid = lo + hi >> 1;
669
+ if (v6a[mid].start <= target.start) {
670
+ idx = mid;
671
+ lo = mid + 1;
672
+ } else hi = mid - 1;
673
+ }
674
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
636
675
  }
637
- if (idx < 0 || maxEnd[idx] < target.end) return false;
638
676
  }
639
677
  }
640
678
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "11.3.1",
3
+ "version": "11.3.2",
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,22 +17,22 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.3.1"
20
+ "ip-bigint": "^8.3.2"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "25.5.0",
24
- "@typescript/native-preview": "7.0.0-dev.20260317.1",
25
- "eslint": "9.39.4",
26
- "eslint-config-silverwind": "125.0.3",
24
+ "@typescript/native-preview": "7.0.0-dev.20260324.1",
25
+ "eslint": "10.1.0",
26
+ "eslint-config-silverwind": "127.1.4",
27
27
  "jest-extended": "7.0.0",
28
28
  "tsdown": "0.21.4",
29
29
  "tsdown-config-silverwind": "2.0.2",
30
30
  "typescript": "5.9.3",
31
- "typescript-config-silverwind": "16.0.0",
32
- "updates": "17.9.1",
33
- "updates-config-silverwind": "1.0.4",
34
- "versions": "14.2.1",
35
- "vitest": "4.1.0",
36
- "vitest-config-silverwind": "10.6.5"
31
+ "typescript-config-silverwind": "16.1.0",
32
+ "updates": "17.11.2",
33
+ "updates-config-silverwind": "2.0.1",
34
+ "versions": "14.2.4",
35
+ "vitest": "4.1.1",
36
+ "vitest-config-silverwind": "11.0.0"
37
37
  }
38
38
  }