cidr-tools 11.3.1 → 11.3.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 CHANGED
@@ -1,9 +1,5 @@
1
1
  //#region index.d.ts
2
- type IPv4Address = string;
3
- type IPv6Address = string;
4
- type IPv4CIDR = string;
5
- type IPv6CIDR = string;
6
- type Network = IPv4Address | IPv4CIDR | IPv6Address | IPv6CIDR;
2
+ type Network = string;
7
3
  type Networks = Network | Array<Network>;
8
4
  type ValidIpVersion = 4 | 6;
9
5
  type ParsedCidr = {
package/dist/index.js CHANGED
@@ -6,8 +6,12 @@ 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
- const prefixNumStrings = Array.from({ length: 129 }, (_, i) => String(i));
11
+ const cmpV4StartEnd = (a, b) => a.start - b.start || a.end - b.end;
12
+ const cmpV4Start = (a, b) => a.start - b.start;
13
+ 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;
14
+ const cmpV6Start = (a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : 0;
11
15
  function parseIPv4Fast(s, end) {
12
16
  let num = 0;
13
17
  let octet = 0;
@@ -17,7 +21,7 @@ function parseIPv4Fast(s, end) {
17
21
  const c = s.charCodeAt(i);
18
22
  if (c === 46) {
19
23
  if (digits === 0 || octet > 255) return -1;
20
- num = (num << 8 | octet) >>> 0;
24
+ num = num << 8 | octet;
21
25
  octet = 0;
22
26
  dots++;
23
27
  digits = 0;
@@ -30,7 +34,7 @@ function parseIPv4Fast(s, end) {
30
34
  return (num << 8 | octet) >>> 0;
31
35
  }
32
36
  function formatIPv4Fast(n) {
33
- return `${octetStrings[n >>> 24 & 255]}.${octetStrings[n >>> 16 & 255]}.${octetStrings[n >>> 8 & 255]}.${octetStrings[n & 255]}`;
37
+ return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
34
38
  }
35
39
  function parsePrefixNum(str, slashIndex) {
36
40
  if (slashIndex === -1) return -1;
@@ -100,7 +104,7 @@ function parseCidr(str) {
100
104
  if (v4num !== -1) {
101
105
  const prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : 32;
102
106
  const ip = formatIPv4Fast(v4num);
103
- const prefix = prefixNumStrings[prefixNum];
107
+ const prefix = String(prefixNum);
104
108
  const hostBits = 32 - prefixNum;
105
109
  let startNum, endNum;
106
110
  if (hostBits >= 32) {
@@ -126,7 +130,7 @@ function parseCidr(str) {
126
130
  const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
127
131
  if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
128
132
  if (prefixNum === -1) prefixNum = bits[version];
129
- const prefix = prefixNumStrings[prefixNum];
133
+ const prefix = String(prefixNum);
130
134
  const ip = stringifyIp({
131
135
  number,
132
136
  version,
@@ -235,13 +239,7 @@ function subparts4(pStart, pEnd, output) {
235
239
  let biggest = biggestPowerOfTwo4(size);
236
240
  let start;
237
241
  let end;
238
- if (size === biggest && pStart % biggest === 0) {
239
- output.push({
240
- start: pStart,
241
- end: pEnd
242
- });
243
- return;
244
- } else if (pStart % biggest === 0) {
242
+ if (pStart % biggest === 0) {
245
243
  start = pStart;
246
244
  end = start + biggest - 1;
247
245
  } else {
@@ -299,13 +297,7 @@ function subparts6(pStart, pEnd, output) {
299
297
  let biggest = biggestPowerOfTwo(size);
300
298
  let start;
301
299
  let end;
302
- if (size === biggest && (pStart & biggest - 1n) === 0n) {
303
- output.push({
304
- start: pStart,
305
- end: pEnd
306
- });
307
- return;
308
- } else if ((pStart & biggest - 1n) === 0n) {
300
+ if ((pStart & biggest - 1n) === 0n) {
309
301
  start = pStart;
310
302
  end = start + biggest - 1n;
311
303
  } else {
@@ -330,21 +322,17 @@ function subparts6(pStart, pEnd, output) {
330
322
  if (end !== pEnd) subparts6(end + 1n, pEnd, output);
331
323
  }
332
324
  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))];
325
+ return formatIPv4Fast(part.start) + prefixStrings[Math.clz32(part.end - part.start)];
336
326
  }
337
327
  function formatPart6(part) {
338
- const ip = stringifyIp({
328
+ return stringifyIp({
339
329
  number: part.start,
340
330
  version: 6
341
- });
342
- const size = part.end - part.start + 1n;
343
- return ip + prefixStrings[128 - (size <= 1n ? 0 : bigintBitLength(size) - 1)];
331
+ }) + prefixStrings[128 - bigintBitLength(part.end - part.start)];
344
332
  }
345
333
  function mergeIntervalsRaw4(nets) {
346
334
  if (nets.length === 0) return [];
347
- nets.sort((a, b) => a.start - b.start || a.end - b.end);
335
+ nets.sort(cmpV4StartEnd);
348
336
  const merged = [];
349
337
  let curStart = nets[0].start;
350
338
  let curEnd = nets[0].end;
@@ -369,7 +357,7 @@ function mergeIntervalsRaw4(nets) {
369
357
  }
370
358
  function mergeIntervalsRaw6(nets) {
371
359
  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);
360
+ nets.sort(cmpV6StartEnd);
373
361
  const merged = [];
374
362
  let curStart = nets[0].start;
375
363
  let curEnd = nets[0].end;
@@ -507,11 +495,28 @@ function* expandCidr(nets) {
507
495
  if (n.version === 4) v4.push(n);
508
496
  else v6.push(n);
509
497
  }
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
- });
498
+ if (v4.length > 0) for (const part of mergeIntervalsRaw4(v4)) {
499
+ let prevHigh = -1;
500
+ let prefix = "";
501
+ for (let n = part.start; n <= part.end; n++) {
502
+ const high = n >>> 8;
503
+ if (high !== prevHigh) {
504
+ prefix = octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255];
505
+ prevHigh = high;
506
+ }
507
+ yield prefix + octetStrings[n & 255];
508
+ }
509
+ }
510
+ if (v6.length > 0) {
511
+ const ipObj = {
512
+ number: 0n,
513
+ version: 6
514
+ };
515
+ for (const part of mergeIntervalsRaw6(v6)) for (let num = part.start; num <= part.end; num++) {
516
+ ipObj.number = num;
517
+ yield stringifyIp(ipObj);
518
+ }
519
+ }
515
520
  }
516
521
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
517
522
  function overlapCidr(a, b) {
@@ -546,8 +551,8 @@ function overlapCidr(a, b) {
546
551
  const as = v4a[0].start, ae = v4a[0].end;
547
552
  for (const el of v4b) if (as <= el.end && el.start <= ae) return true;
548
553
  } else {
549
- v4a.sort((x, y) => x.start - y.start);
550
- v4b.sort((x, y) => x.start - y.start);
554
+ v4a.sort(cmpV4Start);
555
+ v4b.sort(cmpV4Start);
551
556
  let i = 0, j = 0;
552
557
  while (i < v4a.length && j < v4b.length) {
553
558
  if (v4a[i].start <= v4b[j].end && v4b[j].start <= v4a[i].end) return true;
@@ -562,8 +567,8 @@ function overlapCidr(a, b) {
562
567
  const as = v6a[0].start, ae = v6a[0].end;
563
568
  for (const el of v6b) if (as <= el.end && el.start <= ae) return true;
564
569
  } 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);
570
+ v6a.sort(cmpV6Start);
571
+ v6b.sort(cmpV6Start);
567
572
  let i = 0, j = 0;
568
573
  while (i < v6a.length && j < v6b.length) {
569
574
  if (v6a[i].start <= v6b[j].end && v6b[j].start <= v6a[i].end) return true;
@@ -601,40 +606,60 @@ function containsCidr(a, b) {
601
606
  }
602
607
  if (v4b.length > 0) {
603
608
  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;
609
+ if (v4b.length === 1) {
610
+ const ts = v4b[0].start, te = v4b[0].end;
611
+ let found = false;
612
+ for (const a of v4a) if (a.start <= ts && a.end >= te) {
613
+ found = true;
614
+ break;
615
+ }
616
+ if (!found) return false;
617
+ } else {
618
+ v4a.sort(cmpV4Start);
619
+ const maxEnd = new Array(v4a.length);
620
+ maxEnd[0] = v4a[0].end;
621
+ for (let i = 1; i < v4a.length; i++) maxEnd[i] = Math.max(v4a[i].end, maxEnd[i - 1]);
622
+ for (const target of v4b) {
623
+ let lo = 0, hi = v4a.length - 1;
624
+ let idx = -1;
625
+ while (lo <= hi) {
626
+ const mid = lo + hi >> 1;
627
+ if (v4a[mid].start <= target.start) {
628
+ idx = mid;
629
+ lo = mid + 1;
630
+ } else hi = mid - 1;
631
+ }
632
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
617
633
  }
618
- if (idx < 0 || maxEnd[idx] < target.end) return false;
619
634
  }
620
635
  }
621
636
  if (v6b.length > 0) {
622
637
  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;
638
+ if (v6b.length === 1) {
639
+ const ts = v6b[0].start, te = v6b[0].end;
640
+ let found = false;
641
+ for (const a of v6a) if (a.start <= ts && a.end >= te) {
642
+ found = true;
643
+ break;
644
+ }
645
+ if (!found) return false;
646
+ } else {
647
+ v6a.sort(cmpV6Start);
648
+ const maxEnd = new Array(v6a.length);
649
+ maxEnd[0] = v6a[0].end;
650
+ for (let i = 1; i < v6a.length; i++) maxEnd[i] = v6a[i].end > maxEnd[i - 1] ? v6a[i].end : maxEnd[i - 1];
651
+ for (const target of v6b) {
652
+ let lo = 0, hi = v6a.length - 1;
653
+ let idx = -1;
654
+ while (lo <= hi) {
655
+ const mid = lo + hi >> 1;
656
+ if (v6a[mid].start <= target.start) {
657
+ idx = mid;
658
+ lo = mid + 1;
659
+ } else hi = mid - 1;
660
+ }
661
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
636
662
  }
637
- if (idx < 0 || maxEnd[idx] < target.end) return false;
638
663
  }
639
664
  }
640
665
  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.3",
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,23 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.3.1"
20
+ "ip-bigint": "^8.3.4"
21
21
  },
22
22
  "devDependencies": {
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",
23
+ "@types/node": "25.5.2",
24
+ "@typescript/native-preview": "7.0.0-dev.20260409.1",
25
+ "@vitest/coverage-v8": "4.1.4",
26
+ "eslint": "10.2.0",
27
+ "eslint-config-silverwind": "131.0.2",
27
28
  "jest-extended": "7.0.0",
28
- "tsdown": "0.21.4",
29
- "tsdown-config-silverwind": "2.0.2",
29
+ "tsdown": "0.21.7",
30
+ "tsdown-config-silverwind": "2.0.5",
30
31
  "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"
32
+ "typescript-config-silverwind": "17.0.0",
33
+ "updates": "17.14.0",
34
+ "updates-config-silverwind": "2.0.1",
35
+ "versions": "14.2.7",
36
+ "vitest": "4.1.4",
37
+ "vitest-config-silverwind": "11.1.4"
37
38
  }
38
39
  }