Last active
January 3, 2026 03:55
-
-
Save definiteIymaybe/8c15a865b27251c08831af3a3a37c7cd to your computer and use it in GitHub Desktop.
CIDR to IP Range (Notion Formula)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| # CIDR to IP Range (Notion Formula) | |
| A correct, integer-based CIDR to IP range converter implemented as a Notion formula. | |
| Handles all IPv4 CIDR prefixes (`/0–/32`), avoids octet-based hacks, and defines explicit behavior for invalid input. | |
| ## Behavior | |
| - Empty input → empty output | |
| - Invalid IP address → empty output | |
| - Missing or empty prefix (`10.0.0.1`, `10.0.0.1/`) → treated as `/32` | |
| - Fractional prefix → floored (`/31.9` → `/31`) | |
| - Prefix < 0, > 32, or non-numeric → empty output | |
| - `/32` collapses to a single IP | |
| - `/31` returns two IPs (raw CIDR semantics) | |
| ## Why integer-based | |
| Octet-level CIDR calculations break for prefixes crossing octet boundaries (e.g. `/1`, `/9`, `/17`). | |
| This formula converts IP addresses to 32-bit integers, applies CIDR math at the integer level, and converts results back to dotted notation. | |
| ## Example | |
| | Input | Output | | |
| |------------------|-------------------------------------| | |
| | `10.0.0.10` | `10.0.0.10` | | |
| | `10.0.0.0/24` | `10.0.0.0 → 10.0.0.255` | | |
| | `10.0.0.10/1` | `0.0.0.0 → 127.255.255.255` | | |
| | `10.0.0.0/well` | *(empty)* | | |
| */ | |
| /* ========================================= | |
| CIDR → IP / Range (Integer-Correct) | |
| Supports /0–/32, quiet on empty input | |
| ========================================= */ | |
| lets( | |
| /* ---------- Input ---------- */ | |
| raw, replaceAll(prop("IP Range CIDR"), " ", ""), | |
| parts, raw.split("/"), | |
| ip, | |
| if(raw == "", "0.0.0.0", parts.at(0)), | |
| prefixRaw, | |
| if( | |
| parts.length() > 1 and parts.at(1) != "", | |
| toNumber(parts.at(1)), | |
| 32 | |
| ), | |
| prefix, | |
| if( | |
| format(toNumber(prefixRaw))==format(prefixRaw), | |
| floor( | |
| if(prefixRaw < 0, 0, | |
| if(prefixRaw > 32, 32, | |
| prefixRaw)) | |
| ), | |
| 32 | |
| ), | |
| /* ---------- Octets ---------- */ | |
| octets, ip.split("."), | |
| o1, toNumber(octets.at(0)), | |
| o2, toNumber(octets.at(1)), | |
| o3, toNumber(octets.at(2)), | |
| o4, toNumber(octets.at(3)), | |
| validIp, | |
| raw != "" | |
| and octets.length() == 4 | |
| and [o1, o2, o3, o4].every( | |
| current >= 0 and current <= 255 | |
| ), | |
| /* ---------- IP → Integer ---------- */ | |
| ipInt, | |
| o1 * pow(256, 3) + | |
| o2 * pow(256, 2) + | |
| o3 * 256 + | |
| o4, | |
| /* ---------- CIDR Math ---------- */ | |
| blockSize, pow(2, 32 - prefix), | |
| networkInt, | |
| floor(ipInt / blockSize) * blockSize, | |
| broadcastInt, | |
| networkInt + blockSize - 1, | |
| /* ---------- Integer → IP ---------- */ | |
| n1, floor(networkInt / pow(256,3)) % 256, | |
| n2, floor(networkInt / pow(256,2)) % 256, | |
| n3, floor(networkInt / 256) % 256, | |
| n4, networkInt % 256, | |
| b1, floor(broadcastInt / pow(256,3)) % 256, | |
| b2, floor(broadcastInt / pow(256,2)) % 256, | |
| b3, floor(broadcastInt / 256) % 256, | |
| b4, broadcastInt % 256, | |
| net, | |
| format(n1) + "." + format(n2) + "." + | |
| format(n3) + "." + format(n4), | |
| brd, | |
| format(b1) + "." + format(b2) + "." + | |
| format(b3) + "." + format(b4), | |
| /* ---------- Output ---------- */ | |
| if( | |
| not validIp, | |
| "", | |
| if( | |
| prefix == 32, | |
| net, | |
| net + " → " + brd | |
| ) | |
| ) | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment