cidr-tools 11.3.0 → 11.3.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 +96 -78
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5,12 +5,15 @@ const bits = {
5
5
  4: 32,
6
6
  6: 128
7
7
  };
8
- function parseIPv4Fast(s) {
8
+ const octetStrings = Array.from({ length: 256 }, (_, i) => String(i));
9
+ const prefixStrings = Array.from({ length: 129 }, (_, i) => `/${i}`);
10
+ const prefixNumStrings = Array.from({ length: 129 }, (_, i) => String(i));
11
+ function parseIPv4Fast(s, end) {
9
12
  let num = 0;
10
13
  let octet = 0;
11
14
  let dots = 0;
12
15
  let digits = 0;
13
- for (let i = 0; i < s.length; i++) {
16
+ for (let i = 0; i < end; i++) {
14
17
  const c = s.charCodeAt(i);
15
18
  if (c === 46) {
16
19
  if (digits === 0 || octet > 255) return -1;
@@ -27,7 +30,7 @@ function parseIPv4Fast(s) {
27
30
  return (num << 8 | octet) >>> 0;
28
31
  }
29
32
  function formatIPv4Fast(n) {
30
- return `${n >>> 24 & 255}.${n >>> 16 & 255}.${n >>> 8 & 255}.${n & 255}`;
33
+ return `${octetStrings[n >>> 24 & 255]}.${octetStrings[n >>> 16 & 255]}.${octetStrings[n >>> 8 & 255]}.${octetStrings[n & 255]}`;
31
34
  }
32
35
  function parsePrefixNum(str, slashIndex) {
33
36
  if (slashIndex === -1) return -1;
@@ -40,19 +43,44 @@ function parsePrefixNum(str, slashIndex) {
40
43
  }
41
44
  return prefixNum;
42
45
  }
43
- function doNormalize(cidr, { compress = true, hexify = false } = {}) {
46
+ let rangeV4Start = 0;
47
+ let rangeV4End = 0;
48
+ let rangeV4Prefix = 0;
49
+ let rangeSlashIndex = -1;
50
+ function parseIPv4Range(str) {
51
+ rangeSlashIndex = str.indexOf("/");
52
+ const v4num = parseIPv4Fast(str, rangeSlashIndex !== -1 ? rangeSlashIndex : str.length);
53
+ if (v4num === -1) return false;
54
+ rangeV4Prefix = rangeSlashIndex !== -1 ? parsePrefixNum(str, rangeSlashIndex) : 32;
55
+ const hostBits = 32 - rangeV4Prefix;
56
+ if (hostBits >= 32) {
57
+ rangeV4Start = 0;
58
+ rangeV4End = 4294967295;
59
+ } else {
60
+ const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
61
+ rangeV4Start = (v4num & ~mask) >>> 0;
62
+ rangeV4End = (v4num | mask) >>> 0;
63
+ }
64
+ return true;
65
+ }
66
+ function doNormalize(cidr, opts) {
67
+ if (parseIPv4Range(cidr)) {
68
+ const ip = formatIPv4Fast(rangeV4Start);
69
+ return rangeSlashIndex !== -1 ? ip + prefixStrings[rangeV4Prefix] : ip;
70
+ }
44
71
  const { start, end, prefix, version, prefixPresent } = parseCidr(cidr);
45
72
  if (version === 4) {
46
73
  const ip = formatIPv4Fast(Number(start));
47
- return start !== end || prefixPresent ? `${ip}/${prefix}` : ip;
74
+ return start !== end || prefixPresent ? ip + prefixStrings[Number(prefix)] : ip;
48
75
  }
49
- if (start !== end || prefixPresent) return `${normalizeIp(stringifyIp({
76
+ const { compress = true, hexify = false } = opts || {};
77
+ if (start !== end || prefixPresent) return normalizeIp(stringifyIp({
50
78
  number: start,
51
79
  version
52
80
  }), {
53
81
  compress,
54
82
  hexify
55
- })}/${prefix}`;
83
+ }) + prefixStrings[Number(prefix)];
56
84
  else return normalizeIp(cidr, {
57
85
  compress,
58
86
  hexify
@@ -66,49 +94,39 @@ function normalizeCidr(cidr, opts) {
66
94
  /** Returns a `parsed` Object which is used internally by this module. It can be used to test whether the passed network is IPv4 or IPv6 or to work with the BigInts directly. */
67
95
  function parseCidr(str) {
68
96
  const slashIndex = str.indexOf("/");
69
- let ipPart;
70
- let prefixNum;
71
- let prefixPresent;
72
- if (slashIndex !== -1) {
73
- ipPart = str.substring(0, slashIndex);
74
- prefixNum = parsePrefixNum(str, slashIndex);
75
- prefixPresent = true;
76
- } else {
77
- ipPart = str;
78
- prefixNum = -1;
79
- prefixPresent = false;
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
- };
97
+ const ipEnd = slashIndex !== -1 ? slashIndex : str.length;
98
+ const prefixPresent = slashIndex !== -1;
99
+ const v4num = parseIPv4Fast(str, ipEnd);
100
+ if (v4num !== -1) {
101
+ const prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : 32;
102
+ const ip = formatIPv4Fast(v4num);
103
+ const prefix = prefixNumStrings[prefixNum];
104
+ const hostBits = 32 - prefixNum;
105
+ let startNum, endNum;
106
+ if (hostBits >= 32) {
107
+ startNum = 0;
108
+ endNum = 4294967295;
109
+ } else {
110
+ const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
111
+ startNum = (v4num & ~mask) >>> 0;
112
+ endNum = (v4num | mask) >>> 0;
106
113
  }
114
+ return {
115
+ cidr: ip + prefixStrings[prefixNum],
116
+ ip,
117
+ version: 4,
118
+ prefix,
119
+ prefixPresent,
120
+ start: BigInt(startNum),
121
+ end: BigInt(endNum)
122
+ };
107
123
  }
124
+ const ipPart = prefixPresent ? str.substring(0, slashIndex) : str;
125
+ let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
108
126
  const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
109
127
  if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
110
128
  if (prefixNum === -1) prefixNum = bits[version];
111
- const prefix = String(prefixNum);
129
+ const prefix = prefixNumStrings[prefixNum];
112
130
  const ip = stringifyIp({
113
131
  number,
114
132
  version,
@@ -118,7 +136,7 @@ function parseCidr(str) {
118
136
  const hostBits = bits[version] - prefixNum;
119
137
  const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
120
138
  return {
121
- cidr: `${ip}/${prefix}`,
139
+ cidr: ip + prefixStrings[prefixNum],
122
140
  ip,
123
141
  version,
124
142
  prefix,
@@ -128,34 +146,14 @@ function parseCidr(str) {
128
146
  };
129
147
  }
130
148
  function parseCidrLean(str) {
131
- const slashIndex = str.indexOf("/");
132
- let ipPart;
133
- let prefixNum;
134
- if (slashIndex !== -1) {
135
- ipPart = str.substring(0, slashIndex);
136
- prefixNum = parsePrefixNum(str, slashIndex);
137
- } else {
138
- ipPart = str;
139
- prefixNum = -1;
140
- }
141
- if (!ipPart.includes(":")) {
142
- const v4num = parseIPv4Fast(ipPart);
143
- if (v4num !== -1) {
144
- if (prefixNum === -1) prefixNum = 32;
145
- const hostBits = 32 - prefixNum;
146
- if (hostBits >= 32) return {
147
- start: 0,
148
- end: 4294967295,
149
- version: 4
150
- };
151
- const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
152
- return {
153
- start: (v4num & ~mask) >>> 0,
154
- end: (v4num | mask) >>> 0,
155
- version: 4
156
- };
157
- }
158
- }
149
+ if (parseIPv4Range(str)) return {
150
+ start: rangeV4Start,
151
+ end: rangeV4End,
152
+ version: 4
153
+ };
154
+ const slashIndex = rangeSlashIndex;
155
+ const ipPart = slashIndex !== -1 ? str.substring(0, slashIndex) : str;
156
+ let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
159
157
  const { number, version } = parseIp(ipPart);
160
158
  if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
161
159
  if (prefixNum === -1) prefixNum = bits[version];
@@ -334,7 +332,7 @@ function subparts6(pStart, pEnd, output) {
334
332
  function formatPart4(part) {
335
333
  const ip = formatIPv4Fast(part.start);
336
334
  const size = part.end - part.start + 1;
337
- return `${ip}/${32 - (size <= 1 ? 0 : size >= 4294967296 ? 32 : 31 - Math.clz32(size))}`;
335
+ return ip + prefixStrings[32 - (size <= 1 ? 0 : size >= 4294967296 ? 32 : 31 - Math.clz32(size))];
338
336
  }
339
337
  function formatPart6(part) {
340
338
  const ip = stringifyIp({
@@ -342,7 +340,7 @@ function formatPart6(part) {
342
340
  version: 6
343
341
  });
344
342
  const size = part.end - part.start + 1n;
345
- return `${ip}/${128 - (size <= 1n ? 0 : bigintBitLength(size) - 1)}`;
343
+ return ip + prefixStrings[128 - (size <= 1n ? 0 : bigintBitLength(size) - 1)];
346
344
  }
347
345
  function mergeIntervalsRaw4(nets) {
348
346
  if (nets.length === 0) return [];
@@ -518,6 +516,10 @@ function* expandCidr(nets) {
518
516
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
519
517
  function overlapCidr(a, b) {
520
518
  if (!Array.isArray(a) && !Array.isArray(b)) {
519
+ if (parseIPv4Range(a)) {
520
+ const startA = rangeV4Start, endA = rangeV4End;
521
+ if (parseIPv4Range(b)) return startA <= rangeV4End && rangeV4Start <= endA;
522
+ }
521
523
  const pa = parseCidrLean(a);
522
524
  const pb = parseCidrLean(b);
523
525
  if (pa.version !== pb.version) return false;
@@ -537,7 +539,13 @@ function overlapCidr(a, b) {
537
539
  if (n.version === 4) v4b.push(n);
538
540
  else v6b.push(n);
539
541
  }
540
- if (v4a.length > 0 && v4b.length > 0) {
542
+ if (v4a.length > 0 && v4b.length > 0) if (v4b.length === 1) {
543
+ const bs = v4b[0].start, be = v4b[0].end;
544
+ for (const el of v4a) if (el.start <= be && bs <= el.end) return true;
545
+ } else if (v4a.length === 1) {
546
+ const as = v4a[0].start, ae = v4a[0].end;
547
+ for (const el of v4b) if (as <= el.end && el.start <= ae) return true;
548
+ } else {
541
549
  v4a.sort((x, y) => x.start - y.start);
542
550
  v4b.sort((x, y) => x.start - y.start);
543
551
  let i = 0, j = 0;
@@ -547,7 +555,13 @@ function overlapCidr(a, b) {
547
555
  else j++;
548
556
  }
549
557
  }
550
- if (v6a.length > 0 && v6b.length > 0) {
558
+ if (v6a.length > 0 && v6b.length > 0) if (v6b.length === 1) {
559
+ const bs = v6b[0].start, be = v6b[0].end;
560
+ for (const el of v6a) if (el.start <= be && bs <= el.end) return true;
561
+ } else if (v6a.length === 1) {
562
+ const as = v6a[0].start, ae = v6a[0].end;
563
+ for (const el of v6b) if (as <= el.end && el.start <= ae) return true;
564
+ } else {
551
565
  v6a.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
552
566
  v6b.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
553
567
  let i = 0, j = 0;
@@ -562,6 +576,10 @@ function overlapCidr(a, b) {
562
576
  /** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
563
577
  function containsCidr(a, b) {
564
578
  if (!Array.isArray(a) && !Array.isArray(b)) {
579
+ if (parseIPv4Range(a)) {
580
+ const startA = rangeV4Start, endA = rangeV4End;
581
+ if (parseIPv4Range(b)) return startA <= rangeV4Start && endA >= rangeV4End;
582
+ }
565
583
  const pa = parseCidrLean(a);
566
584
  const pb = parseCidrLean(b);
567
585
  if (pa.version !== pb.version) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "11.3.0",
3
+ "version": "11.3.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,7 +17,7 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.2.12"
20
+ "ip-bigint": "^8.3.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "25.5.0",