cidr-tools 11.0.6 → 11.0.9

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 +111 -75
  2. package/package.json +6 -4
package/dist/index.js CHANGED
@@ -6,15 +6,6 @@ function uniq(arr) {
6
6
  function cidrVersion(cidr) {
7
7
  return cidr.includes("/") ? ipVersion(cidr) : 0;
8
8
  }
9
- function compare(a, b) {
10
- const { number: aNum, version: aVersion } = parseIp(a.replace(/\/.+/, ""));
11
- const { number: bNum, version: bVersion } = parseIp(b.replace(/\/.+/, ""));
12
- if (aVersion === bVersion) {
13
- return aNum - bNum > 0n ? 1 : aNum - bNum < 0n ? -1 : 0;
14
- } else {
15
- return aVersion > bVersion ? 1 : 0;
16
- }
17
- }
18
9
  function doNormalize(cidr, { compress = true, hexify = false } = {}) {
19
10
  const { start, end, prefix, version, prefixPresent } = parseCidr(cidr);
20
11
  if (start !== end || prefixPresent) {
@@ -63,20 +54,39 @@ function parseCidr(str) {
63
54
  parsed.end = number | mask;
64
55
  return parsed;
65
56
  }
66
- function doNetsOverlap(a, b) {
67
- if (a.start > b.end) return false;
68
- if (b.start > a.end) return false;
69
- return true;
70
- }
71
- function netContains(a, b) {
72
- if (b.start < a.start) return false;
73
- if (b.end > a.end) return false;
74
- return true;
57
+ function parseCidrLean(str) {
58
+ const cidrVer = cidrVersion(str);
59
+ let version;
60
+ let cidr;
61
+ if (cidrVer) {
62
+ cidr = str;
63
+ version = cidrVer;
64
+ } else {
65
+ const v = ipVersion(str);
66
+ if (v) {
67
+ version = v;
68
+ cidr = `${str}/${bits[version]}`;
69
+ } else {
70
+ throw new Error(`Network is not a CIDR or IP: "${str}"`);
71
+ }
72
+ }
73
+ const [ipAndMisc, prefix] = cidr.split("/");
74
+ if (!/^[0-9]+$/.test(prefix)) {
75
+ throw new Error(`Network is not a CIDR or IP: "${str}"`);
76
+ }
77
+ const { number, version: parsedVersion } = parseIp(ipAndMisc);
78
+ const numBits = bits[parsedVersion];
79
+ const hostBits = BigInt(numBits - Number(prefix));
80
+ const mask = hostBits > 0n ? (1n << hostBits) - 1n : 0n;
81
+ return {
82
+ start: number & ~mask,
83
+ end: number | mask,
84
+ version
85
+ };
75
86
  }
76
- function excludeNets(a, b, v) {
77
- const parts = [];
87
+ function excludeNetsParts(a, b) {
78
88
  if (a.start > b.end || a.end < b.start) {
79
- return [a.cidr];
89
+ return [a];
80
90
  }
81
91
  if (a.start === b.start && a.end === b.end) {
82
92
  return [];
@@ -84,6 +94,7 @@ function excludeNets(a, b, v) {
84
94
  if (a.start > b.start && a.end < b.end) {
85
95
  return [];
86
96
  }
97
+ const parts = [];
87
98
  if (a.start < b.start && a.end <= b.end) {
88
99
  parts.push({ start: a.start, end: b.start - 1n });
89
100
  }
@@ -98,11 +109,9 @@ function excludeNets(a, b, v) {
98
109
  }
99
110
  const remaining = [];
100
111
  for (const part of parts) {
101
- for (const subpart of subparts(part)) {
102
- remaining.push(formatPart(subpart, v));
103
- }
112
+ subparts(part, remaining);
104
113
  }
105
- return mergeCidr(remaining);
114
+ return remaining;
106
115
  }
107
116
  function biggestPowerOfTwo(num) {
108
117
  if (num === 0n) return 0n;
@@ -114,26 +123,30 @@ function biggestPowerOfTwo(num) {
114
123
  }
115
124
  return 1n << b;
116
125
  }
117
- function subparts(part) {
126
+ function subparts(part, output) {
127
+ if (!output) output = [];
118
128
  if (part.end < part.start) {
119
- return [];
129
+ return output;
120
130
  }
121
131
  if (part.end === part.start) {
122
- return [part];
132
+ output.push(part);
133
+ return output;
123
134
  }
124
135
  if (part.end - part.start === 1n) {
125
136
  if (part.end % 2n === 0n) {
126
- return [{ start: part.start, end: part.start }, { start: part.end, end: part.end }];
137
+ output.push({ start: part.start, end: part.start }, { start: part.end, end: part.end });
127
138
  } else {
128
- return [{ start: part.start, end: part.end }];
139
+ output.push({ start: part.start, end: part.end });
129
140
  }
141
+ return output;
130
142
  }
131
143
  const size = diff(part.end, part.start);
132
144
  let biggest = biggestPowerOfTwo(size);
133
145
  let start;
134
146
  let end;
135
147
  if (size === biggest && part.start + size === part.end) {
136
- return [part];
148
+ output.push(part);
149
+ return output;
137
150
  } else if (part.start % biggest === 0n) {
138
151
  start = part.start;
139
152
  end = start + biggest - 1n;
@@ -151,14 +164,14 @@ function subparts(part) {
151
164
  end = start + biggest - 1n;
152
165
  }
153
166
  }
154
- let parts = [{ start, end }];
155
167
  if (start !== part.start) {
156
- parts = parts.concat(subparts({ start: part.start, end: start - 1n }));
168
+ subparts({ start: part.start, end: start - 1n }, output);
157
169
  }
170
+ output.push({ start, end });
158
171
  if (end !== part.end) {
159
- parts = parts.concat(subparts({ start: end + 1n, end: part.end }));
172
+ subparts({ start: end + 1n, end: part.end }, output);
160
173
  }
161
- return parts;
174
+ return output;
162
175
  }
163
176
  function diff(a, b) {
164
177
  return a + 1n - b;
@@ -208,88 +221,111 @@ function doMerge(maps) {
208
221
  if (marker.end) depth -= marker.end;
209
222
  const next = numbers[index2 + 1];
210
223
  if (marker.end && depth === 0 && next !== void 0 && next - number > 1n) {
211
- for (const sub of subparts({ start, end })) {
212
- merged.push(sub);
213
- }
224
+ subparts({ start, end }, merged);
214
225
  start = null;
215
226
  end = null;
216
227
  } else if (index2 === numbers.length - 1) {
217
- for (const sub of subparts({ start, end })) {
218
- merged.push(sub);
219
- }
228
+ subparts({ start, end }, merged);
220
229
  }
221
230
  }
222
231
  return merged;
223
232
  }
224
233
  function mergeCidr(nets) {
225
- const arr = uniq(Array.isArray(nets) ? nets : [nets]).map(parseCidr);
234
+ const arr = uniq(Array.isArray(nets) ? nets : [nets]).map(parseCidrLean);
226
235
  const maps = mapNets(arr);
227
236
  const merged = { 4: [], 6: [] };
228
237
  for (const v of [4, 6]) {
229
238
  merged[v] = doMerge(maps[v]).map((part) => formatPart(part, v));
230
239
  }
231
- return [...merged[4].sort(compare), ...merged[6].sort(compare)];
240
+ return [...merged[4], ...merged[6]];
232
241
  }
233
242
  function excludeCidr(base, excl) {
234
- const basenets = mergeCidr(uniq(Array.isArray(base) ? base : [base]));
235
- const exclnets = mergeCidr(uniq(Array.isArray(excl) ? excl : [excl]));
243
+ const baseArr = uniq(Array.isArray(base) ? base : [base]).map(parseCidrLean);
244
+ const exclArr = uniq(Array.isArray(excl) ? excl : [excl]).map(parseCidrLean);
245
+ const baseMaps = mapNets(baseArr);
246
+ const exclMaps = mapNets(exclArr);
236
247
  const bases = { 4: [], 6: [] };
237
248
  const excls = { 4: [], 6: [] };
238
- for (const basenet of basenets) {
239
- const version = cidrVersion(basenet);
240
- if (version) bases[version].push(basenet);
241
- }
242
- for (const exclnet of exclnets) {
243
- const version = cidrVersion(exclnet);
244
- if (version) excls[version].push(exclnet);
249
+ for (const v of [4, 6]) {
250
+ bases[v] = doMerge(baseMaps[v]);
251
+ excls[v] = doMerge(exclMaps[v]);
245
252
  }
246
253
  for (const v of [4, 6]) {
247
- for (const exclcidr of excls[v]) {
248
- const excl2 = parseCidr(exclcidr);
254
+ for (const exclPart of excls[v]) {
249
255
  const newBases = [];
250
- for (const basecidr of bases[v]) {
251
- newBases.push(...excludeNets(parseCidr(basecidr), excl2, v));
256
+ for (const basePart of bases[v]) {
257
+ newBases.push(...excludeNetsParts(basePart, exclPart));
252
258
  }
253
259
  bases[v] = newBases;
254
260
  }
255
261
  }
256
- return bases[4].concat(bases[6]).sort(compare);
262
+ const result = [];
263
+ for (const v of [4, 6]) {
264
+ for (const part of bases[v]) {
265
+ result.push(formatPart(part, v));
266
+ }
267
+ }
268
+ return result;
257
269
  }
258
270
  function* expandCidr(nets) {
259
271
  const arr = uniq(Array.isArray(nets) ? nets : [nets]);
260
272
  for (const net of mergeCidr(arr)) {
261
- const { start, end, version } = parseCidr(net);
273
+ const { start, end, version } = parseCidrLean(net);
262
274
  for (let number = start; number <= end; number++) {
263
275
  yield normalizeIp(stringifyIp({ number, version }));
264
276
  }
265
277
  }
266
278
  }
267
279
  function overlapCidr(a, b) {
268
- const aNets = uniq(Array.isArray(a) ? a : [a]).map(parseCidr);
269
- const bNets = uniq(Array.isArray(b) ? b : [b]).map(parseCidr);
270
- for (const aParsed of aNets) {
271
- for (const bParsed of bNets) {
272
- if (aParsed.version !== bParsed.version) continue;
273
- if (doNetsOverlap(aParsed, bParsed)) return true;
280
+ const aNets = uniq(Array.isArray(a) ? a : [a]).map(parseCidrLean);
281
+ const bNets = uniq(Array.isArray(b) ? b : [b]).map(parseCidrLean);
282
+ for (const v of [4, 6]) {
283
+ const aVer = aNets.filter((n) => n.version === v).sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
284
+ const bVer = bNets.filter((n) => n.version === v).sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
285
+ let i = 0, j = 0;
286
+ while (i < aVer.length && j < bVer.length) {
287
+ const aNet = aVer[i];
288
+ const bNet = bVer[j];
289
+ if (aNet.start <= bNet.end && bNet.start <= aNet.end) return true;
290
+ if (aNet.end < bNet.end) i++;
291
+ else j++;
274
292
  }
275
293
  }
276
294
  return false;
277
295
  }
278
296
  function containsCidr(a, b) {
279
- const aNets = uniq(Array.isArray(a) ? a : [a]).map(parseCidr);
280
- const bNets = uniq(Array.isArray(b) ? b : [b]).map(parseCidr);
281
- const numExpected = bNets.length;
282
- let numFound = 0;
283
- for (const aParsed of aNets) {
284
- for (const bParsed of bNets) {
285
- if (aParsed.version !== bParsed.version) continue;
286
- if (netContains(aParsed, bParsed)) {
287
- numFound++;
288
- continue;
297
+ const aNets = uniq(Array.isArray(a) ? a : [a]).map(parseCidrLean);
298
+ const bNets = uniq(Array.isArray(b) ? b : [b]).map(parseCidrLean);
299
+ for (const v of [4, 6]) {
300
+ const containers = aNets.filter((n) => n.version === v).sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
301
+ const targets = bNets.filter((n) => n.version === v);
302
+ if (targets.length === 0) continue;
303
+ if (containers.length === 0) return false;
304
+ const maxEnd = new Array(containers.length);
305
+ maxEnd[0] = containers[0].end;
306
+ for (let i = 1; i < containers.length; i++) {
307
+ if (containers[i].end > maxEnd[i - 1]) {
308
+ maxEnd[i] = containers[i].end;
309
+ } else {
310
+ maxEnd[i] = maxEnd[i - 1];
311
+ }
312
+ }
313
+ for (const target of targets) {
314
+ let lo = 0, hi = containers.length - 1;
315
+ let idx = -1;
316
+ while (lo <= hi) {
317
+ const mid = lo + hi >> 1;
318
+ if (containers[mid].start <= target.start) {
319
+ idx = mid;
320
+ lo = mid + 1;
321
+ } else {
322
+ hi = mid - 1;
323
+ }
289
324
  }
325
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
290
326
  }
291
327
  }
292
- return numFound === numExpected;
328
+ return true;
293
329
  }
294
330
  const index = {
295
331
  mergeCidr,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "11.0.6",
3
+ "version": "11.0.9",
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,19 +17,21 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.2.4"
20
+ "ip-bigint": "^8.2.6"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@typescript/native-preview": "7.0.0-dev.20260202.1",
24
24
  "eslint": "9.39.2",
25
- "eslint-config-silverwind": "120.0.0",
25
+ "eslint-config-silverwind": "120.1.2",
26
+ "jest-extended": "7.0.0",
26
27
  "typescript": "5.9.3",
27
28
  "typescript-config-silverwind": "14.0.0",
28
29
  "updates": "17.1.0",
30
+ "updates-config-silverwind": "1.0.3",
29
31
  "versions": "14.1.0",
30
32
  "vite": "7.3.1",
31
33
  "vite-config-silverwind": "6.0.9",
32
34
  "vitest": "4.0.18",
33
35
  "vitest-config-silverwind": "10.6.1"
34
36
  }
35
- }
37
+ }