cidr-tools 11.0.12 → 11.0.13

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 +90 -71
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -50,37 +50,40 @@ function normalizeCidr(cidr, opts) {
50
50
  }
51
51
  /** 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. */
52
52
  function parseCidr(str) {
53
- const parsed = Object.create(null);
54
53
  const slashIndex = str.indexOf("/");
55
54
  let ipPart;
56
55
  let prefix;
56
+ let prefixPresent;
57
57
  if (slashIndex !== -1) {
58
58
  ipPart = str.substring(0, slashIndex);
59
59
  prefix = str.substring(slashIndex + 1);
60
60
  if (!/^[0-9]+$/.test(prefix)) throw new Error(`Network is not a CIDR or IP: "${str}"`);
61
- parsed.prefixPresent = true;
61
+ prefixPresent = true;
62
62
  } else {
63
63
  ipPart = str;
64
- parsed.prefixPresent = false;
64
+ prefixPresent = false;
65
65
  prefix = "";
66
66
  }
67
67
  const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
68
68
  if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
69
- if (!parsed.prefixPresent) prefix = String(bits[version]);
70
- parsed.version = version;
71
- parsed.ip = stringifyIp({
69
+ if (!prefixPresent) prefix = String(bits[version]);
70
+ const ip = stringifyIp({
72
71
  number,
73
72
  version,
74
73
  ipv4mapped,
75
74
  scopeid
76
75
  });
77
- parsed.cidr = `${parsed.ip}/${prefix}`;
78
- parsed.prefix = prefix;
79
76
  const hostBits = bits[version] - Number(prefix);
80
77
  const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
81
- parsed.start = number & ~mask;
82
- parsed.end = number | mask;
83
- return parsed;
78
+ return {
79
+ cidr: `${ip}/${prefix}`,
80
+ ip,
81
+ version,
82
+ prefix,
83
+ prefixPresent,
84
+ start: number & ~mask,
85
+ end: number | mask
86
+ };
84
87
  }
85
88
  function parseCidrLean(str) {
86
89
  const slashIndex = str.indexOf("/");
@@ -88,9 +91,13 @@ function parseCidrLean(str) {
88
91
  let prefixNum;
89
92
  if (slashIndex !== -1) {
90
93
  ipPart = str.substring(0, slashIndex);
91
- const prefixStr = str.substring(slashIndex + 1);
92
- if (!/^[0-9]+$/.test(prefixStr)) throw new Error(`Network is not a CIDR or IP: "${str}"`);
93
- prefixNum = Number(prefixStr);
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
+ }
94
101
  } else {
95
102
  ipPart = str;
96
103
  prefixNum = -1;
@@ -99,12 +106,16 @@ function parseCidrLean(str) {
99
106
  const v4num = parseIPv4Fast(ipPart);
100
107
  if (v4num !== -1) {
101
108
  if (prefixNum === -1) prefixNum = 32;
102
- const bigNum = BigInt(v4num);
103
109
  const hostBits = 32 - prefixNum;
104
- const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
110
+ if (hostBits >= 32) return {
111
+ start: 0n,
112
+ end: 4294967295n,
113
+ version: 4
114
+ };
115
+ const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
105
116
  return {
106
- start: bigNum & ~mask,
107
- end: bigNum | mask,
117
+ start: BigInt((v4num & ~mask) >>> 0),
118
+ end: BigInt((v4num | mask) >>> 0),
108
119
  version: 4
109
120
  };
110
121
  }
@@ -124,64 +135,62 @@ function biggestPowerOfTwo(num) {
124
135
  if (num === 0n) return 0n;
125
136
  return 1n << BigInt(num.toString(2).length - 1);
126
137
  }
127
- function subparts(part, output) {
128
- if (!output) output = [];
129
- if (part.end < part.start) return output;
130
- if (part.end === part.start) {
131
- output.push(part);
132
- return output;
138
+ function subparts(pStart, pEnd, output) {
139
+ if (pEnd < pStart) return;
140
+ if (pEnd === pStart) {
141
+ output.push({
142
+ start: pStart,
143
+ end: pEnd
144
+ });
145
+ return;
133
146
  }
134
- if (part.end - part.start === 1n) {
135
- if (part.end % 2n === 0n) output.push({
136
- start: part.start,
137
- end: part.start
147
+ if (pEnd - pStart === 1n) {
148
+ if (pEnd % 2n === 0n) output.push({
149
+ start: pStart,
150
+ end: pStart
138
151
  }, {
139
- start: part.end,
140
- end: part.end
152
+ start: pEnd,
153
+ end: pEnd
141
154
  });
142
155
  else output.push({
143
- start: part.start,
144
- end: part.end
156
+ start: pStart,
157
+ end: pEnd
145
158
  });
146
- return output;
159
+ return;
147
160
  }
148
- const size = diff(part.end, part.start);
161
+ const size = diff(pEnd, pStart);
149
162
  let biggest = biggestPowerOfTwo(size);
150
163
  let start;
151
164
  let end;
152
- if (size === biggest && part.start % biggest === 0n) {
153
- output.push(part);
154
- return output;
155
- } else if (part.start % biggest === 0n) {
156
- start = part.start;
165
+ if (size === biggest && pStart % biggest === 0n) {
166
+ output.push({
167
+ start: pStart,
168
+ end: pEnd
169
+ });
170
+ return;
171
+ } else if (pStart % biggest === 0n) {
172
+ start = pStart;
157
173
  end = start + biggest - 1n;
158
174
  } else {
159
- start = part.end / biggest * biggest;
160
- if (start + biggest - 1n > part.end) {
161
- start = (part.end / biggest - 1n) * biggest;
162
- while (start < part.start) {
175
+ start = pEnd / biggest * biggest;
176
+ if (start + biggest - 1n > pEnd) {
177
+ start = (pEnd / biggest - 1n) * biggest;
178
+ while (start < pStart) {
163
179
  biggest /= 2n;
164
- start = (part.end / biggest - 1n) * biggest;
180
+ start = (pEnd / biggest - 1n) * biggest;
165
181
  }
166
182
  end = start + biggest - 1n;
167
183
  } else {
168
- start = part.end / biggest * biggest;
184
+ start = pEnd / biggest * biggest;
169
185
  end = start + biggest - 1n;
170
186
  }
171
187
  }
172
- if (start !== part.start) subparts({
173
- start: part.start,
174
- end: start - 1n
175
- }, output);
188
+ if (start !== pStart) subparts(pStart, start - 1n, output);
176
189
  output.push({
177
190
  start,
178
191
  end
179
192
  });
180
- if (end !== part.end) subparts({
181
- start: end + 1n,
182
- end: part.end
183
- }, output);
184
- return output;
193
+ if (end !== pEnd) subparts(end + 1n, pEnd, output);
185
194
  }
186
195
  function diff(a, b) {
187
196
  return a + 1n - b;
@@ -202,12 +211,12 @@ function formatPart(part, version) {
202
211
  }
203
212
  function mergeIntervalsRaw(nets) {
204
213
  if (nets.length === 0) return [];
205
- const sorted = nets.slice().sort((a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : a.end > b.end ? 1 : a.end < b.end ? -1 : 0);
214
+ 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);
206
215
  const merged = [];
207
- let curStart = sorted[0].start;
208
- let curEnd = sorted[0].end;
209
- for (let i = 1; i < sorted.length; i++) {
210
- const { start, end } = sorted[i];
216
+ let curStart = nets[0].start;
217
+ let curEnd = nets[0].end;
218
+ for (let i = 1; i < nets.length; i++) {
219
+ const { start, end } = nets[i];
211
220
  if (start <= curEnd + 1n) {
212
221
  if (end > curEnd) curEnd = end;
213
222
  } else {
@@ -227,7 +236,7 @@ function mergeIntervalsRaw(nets) {
227
236
  }
228
237
  function mergeIntervals(nets) {
229
238
  const merged = [];
230
- for (const part of mergeIntervalsRaw(nets)) subparts(part, merged);
239
+ for (const part of mergeIntervalsRaw(nets)) subparts(part.start, part.end, merged);
231
240
  return merged;
232
241
  }
233
242
  function subtractSorted(bases, excls) {
@@ -284,21 +293,31 @@ function excludeCidr(base, excl) {
284
293
  const result = [];
285
294
  for (const v of [4, 6]) {
286
295
  const remaining = subtractSorted(mergeIntervalsRaw(baseByVersion[v]), mergeIntervalsRaw(exclByVersion[v]));
287
- for (const part of remaining) {
288
- const aligned = subparts(part);
289
- for (const p of aligned) result.push(formatPart(p, v));
290
- }
296
+ 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));
291
299
  }
292
300
  return result;
293
301
  }
294
302
  function* expandCidr(nets) {
295
- const arr = Array.isArray(nets) ? nets : [nets];
296
- for (const net of mergeCidr(arr)) {
297
- const { start, end, version } = parseCidrLean(net);
298
- for (let number = start; number <= end; number++) yield normalizeIp(stringifyIp({
299
- number,
300
- version
301
- }));
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
+ });
302
321
  }
303
322
  }
304
323
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "11.0.12",
3
+ "version": "11.0.13",
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,13 +17,14 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.2.8"
20
+ "ip-bigint": "^8.2.9"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "25.3.3",
24
24
  "@typescript/native-preview": "7.0.0-dev.20260305.1",
25
25
  "eslint": "9.39.3",
26
26
  "eslint-config-silverwind": "121.2.0",
27
+ "fast-cidr-tools": "0.3.4",
27
28
  "jest-extended": "7.0.0",
28
29
  "tsdown": "0.21.0",
29
30
  "tsdown-config-silverwind": "2.0.0",