cidr-tools 11.0.8 → 11.0.10

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