cidr-tools 12.1.1 → 12.1.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.
Files changed (2) hide show
  1. package/dist/index.js +67 -105
  2. package/package.json +15 -15
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ function parseIPv4Fast(s, end) {
38
38
  if (dots !== 3 || digits === 0 || octet > 255) return -1;
39
39
  return (num << 8 | octet) >>> 0;
40
40
  }
41
+ const hostMasks4 = Int32Array.from({ length: 33 }, (_, i) => i === 32 ? -1 : (1 << i) - 1);
41
42
  function formatIPv4Fast(n) {
42
43
  return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
43
44
  }
@@ -61,15 +62,9 @@ function parseIPv4Range(str) {
61
62
  const v4num = parseIPv4Fast(str, rangeSlashIndex !== -1 ? rangeSlashIndex : str.length);
62
63
  if (v4num === -1) return false;
63
64
  rangeV4Prefix = rangeSlashIndex !== -1 ? parsePrefixNum(str, rangeSlashIndex) : 32;
64
- const hostBits = 32 - rangeV4Prefix;
65
- if (hostBits >= 32) {
66
- rangeV4Start = 0;
67
- rangeV4End = 4294967295;
68
- } else {
69
- const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
70
- rangeV4Start = (v4num & ~mask) >>> 0;
71
- rangeV4End = (v4num | mask) >>> 0;
72
- }
65
+ const mask = hostMasks4[Math.max(32 - rangeV4Prefix, 0)];
66
+ rangeV4Start = (v4num & ~mask) >>> 0;
67
+ rangeV4End = (v4num | mask) >>> 0;
73
68
  return true;
74
69
  }
75
70
  function doNormalize(cidr, opts) {
@@ -80,14 +75,12 @@ function doNormalize(cidr, opts) {
80
75
  const slashIndex = rangeSlashIndex;
81
76
  const prefixPresent = slashIndex !== -1;
82
77
  let prefixNum = prefixPresent ? parsePrefixNum(cidr, slashIndex) : -1;
83
- const { number, version } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr);
78
+ const { number, version, ipv4mapped, scopeid } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr);
84
79
  if (prefixNum === -1) prefixNum = bits[version];
85
80
  if (version === 4) {
86
81
  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);
82
+ const mask = hostMasks4[Math.max(hostBits, 0)];
83
+ const ip = formatIPv4Fast((Number(number) & ~mask) >>> 0);
91
84
  return hostBits > 0 || prefixPresent ? ip + prefixStrings[prefixNum] : ip;
92
85
  }
93
86
  const { compress = true, hexify = false } = opts || {};
@@ -96,10 +89,13 @@ function doNormalize(cidr, opts) {
96
89
  compress,
97
90
  hexify
98
91
  });
92
+ const start = hostBits > 0 ? number & hostNotMasks[hostBits] : number;
99
93
  const ip = stringifyIp({
100
- number: hostBits > 0 ? number & hostNotMasks[hostBits] : number,
101
- version
102
- });
94
+ number: start,
95
+ version,
96
+ ipv4mapped: ipv4mapped && start >> 32n === 65535n,
97
+ scopeid
98
+ }, { hexify });
103
99
  return (compress ? ip : normalizeIp(ip, {
104
100
  compress,
105
101
  hexify
@@ -119,24 +115,15 @@ function parseCidr(str) {
119
115
  const prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : 32;
120
116
  const ip = formatIPv4Fast(v4num);
121
117
  const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
122
- const hostBits = 32 - prefixNum;
123
- let startNum, endNum;
124
- if (hostBits >= 32) {
125
- startNum = 0;
126
- endNum = 4294967295;
127
- } else {
128
- const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
129
- startNum = (v4num & ~mask) >>> 0;
130
- endNum = (v4num | mask) >>> 0;
131
- }
118
+ const mask = hostMasks4[Math.max(32 - prefixNum, 0)];
132
119
  return {
133
120
  cidr: ip + prefixStrings[prefixNum],
134
121
  ip,
135
122
  version: 4,
136
123
  prefix,
137
124
  prefixPresent,
138
- start: BigInt(startNum),
139
- end: BigInt(endNum)
125
+ start: BigInt((v4num & ~mask) >>> 0),
126
+ end: BigInt((v4num | mask) >>> 0)
140
127
  };
141
128
  }
142
129
  const ipPart = prefixPresent ? str.substring(0, slashIndex) : str;
@@ -185,12 +172,7 @@ function parseCidrLean(str) {
185
172
  const hostBits = numBits - prefixNum;
186
173
  if (version === 4) {
187
174
  const num = Number(number);
188
- if (hostBits >= 32) return {
189
- start: 0,
190
- end: 4294967295,
191
- version: 4
192
- };
193
- const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
175
+ const mask = hostMasks4[Math.max(hostBits, 0)];
194
176
  return {
195
177
  start: (num & ~mask) >>> 0,
196
178
  end: (num | mask) >>> 0,
@@ -364,13 +346,11 @@ function subtractSorted6(bases, excls) {
364
346
  }
365
347
  /** Returns an array of merged networks */
366
348
  function mergeCidr(nets) {
367
- const arr = typeof nets === "string" ? [nets] : nets;
368
- const v4 = [];
369
- const v6 = [];
370
- for (const s of arr) {
371
- const n = parseCidrLean(s);
372
- if (n.version === 4) v4.push(n);
373
- else v6.push(n);
349
+ const v4 = [], v6 = [];
350
+ for (const str of typeof nets === "string" ? [nets] : nets) {
351
+ const net = parseCidrLean(str);
352
+ if (net.version === 4) v4.push(net);
353
+ else v6.push(net);
374
354
  }
375
355
  const merged = [];
376
356
  for (const part of mergeIntervalsRaw4(v4)) subparts4(part.start, part.end, merged);
@@ -379,42 +359,30 @@ function mergeCidr(nets) {
379
359
  }
380
360
  /** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
381
361
  function excludeCidr(base, excl) {
382
- const baseArr = typeof base === "string" ? [base] : base;
383
- const exclArr = typeof excl === "string" ? [excl] : excl;
384
362
  const v4base = [], v6base = [];
385
363
  const v4excl = [], v6excl = [];
386
- for (const s of baseArr) {
387
- const n = parseCidrLean(s);
388
- if (n.version === 4) v4base.push(n);
389
- else v6base.push(n);
364
+ for (const str of typeof base === "string" ? [base] : base) {
365
+ const net = parseCidrLean(str);
366
+ if (net.version === 4) v4base.push(net);
367
+ else v6base.push(net);
390
368
  }
391
- for (const s of exclArr) {
392
- const n = parseCidrLean(s);
393
- if (n.version === 4) v4excl.push(n);
394
- else v6excl.push(n);
369
+ for (const str of typeof excl === "string" ? [excl] : excl) {
370
+ const net = parseCidrLean(str);
371
+ if (net.version === 4) v4excl.push(net);
372
+ else v6excl.push(net);
395
373
  }
396
374
  const result = [];
397
- {
398
- const baseParts = mergeIntervalsRaw4(v4base);
399
- const exclParts = mergeIntervalsRaw4(v4excl);
400
- for (const part of subtractSorted4(baseParts, exclParts)) subparts4(part.start, part.end, result);
401
- }
402
- {
403
- const baseParts = mergeIntervalsRaw6(v6base);
404
- const exclParts = mergeIntervalsRaw6(v6excl);
405
- for (const part of subtractSorted6(baseParts, exclParts)) subparts6(part.start, part.end, result);
406
- }
375
+ if (v4base.length > 0) for (const part of subtractSorted4(mergeIntervalsRaw4(v4base), mergeIntervalsRaw4(v4excl))) subparts4(part.start, part.end, result);
376
+ if (v6base.length > 0) for (const part of subtractSorted6(mergeIntervalsRaw6(v6base), mergeIntervalsRaw6(v6excl))) subparts6(part.start, part.end, result);
407
377
  return result;
408
378
  }
409
379
  /** Returns a generator for individual IPs contained in the networks. */
410
380
  function* expandCidr(nets) {
411
- const arr = typeof nets === "string" ? [nets] : nets;
412
- const v4 = [];
413
- const v6 = [];
414
- for (const s of arr) {
415
- const n = parseCidrLean(s);
416
- if (n.version === 4) v4.push(n);
417
- else v6.push(n);
381
+ const v4 = [], v6 = [];
382
+ for (const str of typeof nets === "string" ? [nets] : nets) {
383
+ const net = parseCidrLean(str);
384
+ if (net.version === 4) v4.push(net);
385
+ else v6.push(net);
418
386
  }
419
387
  if (v4.length > 0) for (const part of mergeIntervalsRaw4(v4)) {
420
388
  let prevHigh = -1;
@@ -470,26 +438,22 @@ function overlapCidr(a, b) {
470
438
  if (pa.version !== pb.version) return false;
471
439
  return pa.start <= pb.end && pb.start <= pa.end;
472
440
  }
473
- const aArr = typeof a === "string" ? [a] : a;
474
- const bArr = typeof b === "string" ? [b] : b;
475
441
  const v4a = [], v6a = [];
476
442
  const v4b = [], v6b = [];
477
- for (const s of aArr) {
478
- const n = parseCidrLean(s);
479
- if (n.version === 4) v4a.push(n);
480
- else v6a.push(n);
481
- }
482
- for (const s of bArr) {
483
- const n = parseCidrLean(s);
484
- if (n.version === 4) v4b.push(n);
485
- else v6b.push(n);
486
- }
487
- if (v4a.length > 0 && v4b.length > 0) if (v4b.length === 1) {
488
- const bs = v4b[0].start, be = v4b[0].end;
489
- for (const el of v4a) if (el.start <= be && bs <= el.end) return true;
490
- } else if (v4a.length === 1) {
491
- const as = v4a[0].start, ae = v4a[0].end;
492
- for (const el of v4b) if (as <= el.end && el.start <= ae) return true;
443
+ for (const str of typeof a === "string" ? [a] : a) {
444
+ const net = parseCidrLean(str);
445
+ if (net.version === 4) v4a.push(net);
446
+ else v6a.push(net);
447
+ }
448
+ for (const str of typeof b === "string" ? [b] : b) {
449
+ const net = parseCidrLean(str);
450
+ if (net.version === 4) v4b.push(net);
451
+ else v6b.push(net);
452
+ }
453
+ if (v4a.length > 0 && v4b.length > 0) if (v4a.length === 1 || v4b.length === 1) {
454
+ const bIsSingle = v4b.length === 1;
455
+ const one = bIsSingle ? v4b[0] : v4a[0];
456
+ for (const el of bIsSingle ? v4a : v4b) if (one.start <= el.end && el.start <= one.end) return true;
493
457
  } else {
494
458
  v4a.sort(cmpV4Start);
495
459
  v4b.sort(cmpV4Start);
@@ -500,12 +464,10 @@ function overlapCidr(a, b) {
500
464
  else j++;
501
465
  }
502
466
  }
503
- if (v6a.length > 0 && v6b.length > 0) if (v6b.length === 1) {
504
- const bs = v6b[0].start, be = v6b[0].end;
505
- for (const el of v6a) if (el.start <= be && bs <= el.end) return true;
506
- } else if (v6a.length === 1) {
507
- const as = v6a[0].start, ae = v6a[0].end;
508
- for (const el of v6b) if (as <= el.end && el.start <= ae) return true;
467
+ if (v6a.length > 0 && v6b.length > 0) if (v6a.length === 1 || v6b.length === 1) {
468
+ const bIsSingle = v6b.length === 1;
469
+ const one = bIsSingle ? v6b[0] : v6a[0];
470
+ for (const el of bIsSingle ? v6a : v6b) if (one.start <= el.end && el.start <= one.end) return true;
509
471
  } else {
510
472
  v6a.sort(cmpV6Start);
511
473
  v6b.sort(cmpV6Start);
@@ -533,19 +495,17 @@ function containsCidr(a, b) {
533
495
  if (pa.version !== pb.version) return false;
534
496
  return pa.start <= pb.start && pa.end >= pb.end;
535
497
  }
536
- const aArr = typeof a === "string" ? [a] : a;
537
- const bArr = typeof b === "string" ? [b] : b;
538
498
  const v4a = [], v6a = [];
539
499
  const v4b = [], v6b = [];
540
- for (const s of aArr) {
541
- const n = parseCidrLean(s);
542
- if (n.version === 4) v4a.push(n);
543
- else v6a.push(n);
500
+ for (const str of typeof a === "string" ? [a] : a) {
501
+ const net = parseCidrLean(str);
502
+ if (net.version === 4) v4a.push(net);
503
+ else v6a.push(net);
544
504
  }
545
- for (const s of bArr) {
546
- const n = parseCidrLean(s);
547
- if (n.version === 4) v4b.push(n);
548
- else v6b.push(n);
505
+ for (const str of typeof b === "string" ? [b] : b) {
506
+ const net = parseCidrLean(str);
507
+ if (net.version === 4) v4b.push(net);
508
+ else v6b.push(net);
549
509
  }
550
510
  if (v4b.length > 0) {
551
511
  if (v4a.length === 0) return false;
@@ -553,7 +513,8 @@ function containsCidr(a, b) {
553
513
  for (const target of v4b) {
554
514
  const ts = target.start, te = target.end;
555
515
  let covered = false;
556
- for (const iv of v4a) if (iv.start <= ts && iv.end >= te) {
516
+ for (const iv of v4a) {
517
+ if (iv.start > ts || iv.end < te) continue;
557
518
  covered = true;
558
519
  break;
559
520
  }
@@ -582,7 +543,8 @@ function containsCidr(a, b) {
582
543
  for (const target of v6b) {
583
544
  const ts = target.start, te = target.end;
584
545
  let covered = false;
585
- for (const iv of v6a) if (iv.start <= ts && iv.end >= te) {
546
+ for (const iv of v6a) {
547
+ if (iv.start > ts || iv.end < te) continue;
586
548
  covered = true;
587
549
  break;
588
550
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "12.1.1",
3
+ "version": "12.1.3",
4
4
  "author": "silverwind <me@silverwind.io>",
5
5
  "description": "Tools to work with IPv4 and IPv6 CIDR",
6
6
  "keywords": [
@@ -29,23 +29,23 @@
29
29
  "bun": "*"
30
30
  },
31
31
  "dependencies": {
32
- "ip-bigint": "^9.0.6"
32
+ "ip-bigint": "^9.0.7"
33
33
  },
34
34
  "devDependencies": {
35
- "@types/node": "25.9.3",
36
- "@typescript/native-preview": "7.0.0-dev.20260616.1",
37
- "@vitest/coverage-v8": "4.1.9",
38
- "eslint": "10.5.0",
39
- "eslint-config-silverwind": "136.0.10",
35
+ "@types/node": "26.1.2",
36
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
37
+ "@vitest/coverage-v8": "4.1.10",
38
+ "eslint": "10.8.0",
39
+ "eslint-config-silverwind": "143.0.0",
40
40
  "jest-extended": "7.0.0",
41
- "tsdown": "0.22.3",
42
- "tsdown-config-silverwind": "3.0.3",
41
+ "tsdown": "0.22.14",
42
+ "tsdown-config-silverwind": "4.0.3",
43
43
  "typescript": "6.0.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.9",
49
- "vitest-config-silverwind": "11.3.6"
44
+ "typescript-config-silverwind": "20.0.3",
45
+ "updates": "17.19.2",
46
+ "updates-config-silverwind": "4.0.4",
47
+ "versions": "15.1.4",
48
+ "vitest": "4.1.10",
49
+ "vitest-config-silverwind": "11.4.0"
50
50
  }
51
51
  }