Sign in

    ipset

    Pure MoonBit IPv4/IPv6 address sets and minimal CIDR covers

    ip
    ipv4
    ipv6
    cidr
    network
    set
    Download zip
    Version
    0.2.1
    License
    MIT
    Last updated
    13 hours ago
    Downloads
    11

    No dependencies

    #moon-ipset

    纯 MoonBit IPv4/IPv6 地址集合库,用于合并网段、从白名单中排除指定范围,以及生成精确的最小 CIDR 覆盖。计算核心无第三方包依赖,不逐个枚举 IP 地址。

    #功能

    • IPv4/IPv6 解析、格式化和 CIDR 归一化。
    • 集合并集、交集、差集、对称差。
    • 成员、子集和相交判断。
    • 闭合地址范围转换、重复与相邻网段合并、最小 CIDR 输出。

    IPv4 与 IPv6 分开处理;CIDR 默认清除主机位,也支持严格校验。

    #快速开始

    安装 MoonBit 工具链(开发基线 0.10.17),在 Linux/WSL 运行:

    git clone https://github.com/tc-adventureking/moon-ipset.git cd moon-ipset moon run cmd/demo --target wasm-gc

    演示会从双栈白名单中减去排除范围,并检查地址是否仍在集合中。

    #作为库使用

    模块名为 tc-adventureking/ipset。当前通过 Git 克隆和本地 workspace 引用;仓库自带可运行的独立消费示例:

    moon -C examples/consumer run . --target wasm-gc

    在自己的项目中,将库目录和应用目录加入同一个 moon.work,再声明模块依赖与包导入。可直接参考:

    #网段文件 CLI

    需要 Node.js 22.3.0+。文件每行一个 IP 或 CIDR,支持空行、# 注释与标准输入 -。仓库入口会自动调用 MoonBit 构建。

    node bin/ipset.mjs normalize examples/policies/new.txt node bin/ipset.mjs diff examples/policies/old.txt examples/policies/new.txt --json

    diff 输出新增与移除的网段:

    {"added":["10.0.1.10/32","2001:db8:1::/126"],"removed":["10.0.0.128/25","2001:db8::/127"]}

    退出码:0 成功/无变化,1 diff 有变化,2 输入或运行错误。--strict 拒绝有主机位的 CIDR;--help 查看用法。比较的是地址集合,不解释防火墙规则顺序。

    原 JSONL 入口仍可用:node bin/ipset.mjs jsonl < examples/requests.jsonl。协议与文件格式见 SPEC.md,完整场景见 USAGE.md。

    Releases 提供便携 CLI ZIP;完整解压后用 node ipset.mjs --help 运行,只需 Node.js。也可运行 python3 scripts/package_release.py 自行构建并验证安装包。

    #测试

    运行库测试:

    moon test --deny-warn --target wasm-gc

    完整验证还需要 Node.js、Python 3.10+ 和 C 工具链:

    bash scripts/verify.sh

    验证范围与版本见 TESTING.md。

    #性能

    固定输入的 native release 基准数据与复现命令,覆盖 100 至 10,000 条网段及多种分布。

    #许可

    MIT。便携 CLI 包含 MoonBit core 的许可与 NOTICE,见 依赖说明。

    IpError

    pub(all) suberror IpError {
    InvalidAddress(String)
    InvalidPrefix(String)
    InvalidRange(String)
    HostBitsSet(String)
    } derive(Eq)

    Invalid input is reported without constructing an invalid address or range.
    impl Show for IpError

    IpError::equal

    fn IpError::equal(IpError, IpError) -> Bool

    IpError::not_equal

    fn IpError::not_equal(x : IpError, y : IpError) -> Bool

    IpError::output

    fn IpError::output(self : IpError, logger : &Logger) -> Unit

    IpError::to_string

    fn IpError::to_string(self : IpError) -> String

    Address

    type Address derive(Eq)

    An immutable address. The four words are in network (most significant first) order.
    impl Compare for Address
    impl Show for Address

    Address::compare

    fn Address::compare(self : Address, other : Address) -> Int

    Address::equal

    fn Address::equal(Address, Address) -> Bool

    Address::family

    fn Address::family(self : Address) -> Family

    Returns the address family, without interpreting mapped addresses as IPv4.

    Address::next

    fn Address::next(self : Address) -> Address?

    Returns the next address in the same family, or None at its maximum value.

    Address::not_equal

    fn Address::not_equal(x : Address, y : Address) -> Bool

    Address::op_ge

    fn Address::op_ge(x : Address, y : Address) -> Bool

    Address::op_gt

    fn Address::op_gt(x : Address, y : Address) -> Bool

    Address::op_le

    fn Address::op_le(x : Address, y : Address) -> Bool

    Address::op_lt

    fn Address::op_lt(x : Address, y : Address) -> Bool

    Address::output

    fn Address::output(self : Address, logger : &Logger) -> Unit

    Address::parse

    fn Address::parse(text : String) -> Address raise IpError

    Parses strict IPv4 or IPv6 text; rejects whitespace, zones, brackets, and ports.

    Address::previous

    fn Address::previous(self : Address) -> Address?

    Returns the preceding address in the same family, or None at zero.

    Address::to_string

    fn Address::to_string(self : Address) -> String

    Formats IPv4 dotted decimal or lowercase compressed IPv6 (including mapped addresses).

    Address::width

    fn Address::width(self : Address) -> Int

    Returns the width of this address family in bits.

    Cidr

    type Cidr derive(Eq)

    A canonical CIDR network; host bits are zero and the prefix fits the family.
    impl Show for Cidr

    Cidr::equal

    fn Cidr::equal(Cidr, Cidr) -> Bool

    Cidr::last

    fn Cidr::last(self : Cidr) -> Address

    The inclusive final address of this network.

    Cidr::network

    fn Cidr::network(self : Cidr) -> Address

    The first address of this network.

    Cidr::new

    fn Cidr::new(address : Address, prefix : Int, strict? : Bool) -> Cidr raise IpError

    Constructs a CIDR, clearing host bits unless strict validation is requested.

    Cidr::not_equal

    fn Cidr::not_equal(x : Cidr, y : Cidr) -> Bool

    Cidr::output

    fn Cidr::output(self : Cidr, logger : &Logger) -> Unit

    Cidr::parse

    fn Cidr::parse(text : String, strict? : Bool) -> Cidr raise IpError

    Parses address/prefix syntax. The prefix uses decimal digits without leading zeros.

    Cidr::prefix

    fn Cidr::prefix(self : Cidr) -> Int

    Number of leading network bits.

    Cidr::to_string

    fn Cidr::to_string(self : Cidr) -> String

    Canonical network address followed by a decimal prefix.

    Family

    pub(all) enum Family {
    V4
    V6
    } derive(Compare, Eq)

    Address families are distinct, including IPv4-mapped IPv6 addresses.
    impl Show for Family

    Family::compare

    fn Family::compare(Family, Family) -> Int

    Family::equal

    fn Family::equal(Family, Family) -> Bool

    Family::not_equal

    fn Family::not_equal(x : Family, y : Family) -> Bool

    Family::op_ge

    fn Family::op_ge(x : Family, y : Family) -> Bool

    Family::op_gt

    fn Family::op_gt(x : Family, y : Family) -> Bool

    Family::op_le

    fn Family::op_le(x : Family, y : Family) -> Bool

    Family::op_lt

    fn Family::op_lt(x : Family, y : Family) -> Bool

    Family::output

    fn Family::output(self : Family, logger : &Logger) -> Unit

    Family::to_string

    fn Family::to_string(self : Family) -> String

    Interval

    pub struct Interval {
    // private fields
    } derive(Eq)

    An inclusive interval in exactly one IP address family. Construct with Interval::new; endpoints cannot be changed afterwards.

    Interval::equal

    fn Interval::equal(Interval, Interval) -> Bool

    Interval::first

    fn Interval::first(self : Interval) -> Address

    Return the first address, including that address in the range.

    Interval::last

    fn Interval::last(self : Interval) -> Address

    Return the last address, including that address in the range.

    Interval::new

    fn Interval::new(first : Address, last : Address) -> Interval raise IpError

    Validate and create an inclusive address interval.

    Interval::not_equal

    fn Interval::not_equal(x : Interval, y : Interval) -> Bool

    IpSet

    pub struct IpSet {
    // private fields
    } derive(Eq)

    An immutable set of IPv4 and/or IPv6 addresses. Intervals are sorted, non-overlapping, and non-adjacent within each family. IPv4 and IPv6 (including IPv4-mapped IPv6 addresses) remain distinct.

    IpSet::cidrs

    fn IpSet::cidrs(self : IpSet) -> Array[Cidr]

    Return the sorted minimal CIDR cover of this set, without enumerating hosts. At each cursor take the largest aligned block contained in its interval. A /0 is emitted directly for a full address family.

    IpSet::contains

    fn IpSet::contains(self : IpSet, address : Address) -> Bool

    Test membership with binary search over normalized intervals.

    IpSet::difference

    fn IpSet::difference(self : IpSet, other : IpSet) -> IpSet

    Return addresses in this set but not in other. Closed intervals avoid an unrepresentable 2^128 upper endpoint. Optional successor/predecessor values handle the two domain limits explicitly.

    IpSet::empty

    fn IpSet::empty() -> IpSet

    Create a set with no addresses.

    IpSet::equal

    fn IpSet::equal(IpSet, IpSet) -> Bool

    IpSet::from_cidrs

    fn IpSet::from_cidrs(cidrs : Array[String]) -> IpSet raise IpError

    Parse and combine CIDR networks, normalizing any host bits in the input. One invalid CIDR rejects the entire construction.

    IpSet::from_ranges

    fn IpSet::from_ranges(ranges : Array[Interval]) -> IpSet

    Canonicalize a collection of valid intervals. The caller's array is copied before sorting; subsequent mutations of it cannot change this set.

    IpSet::intersection

    fn IpSet::intersection(self : IpSet, other : IpSet) -> IpSet

    Return addresses present in both operands, in O(m+n) interval comparisons.

    IpSet::intervals

    fn IpSet::intervals(self : IpSet) -> Array[Interval]

    Return a copy of the canonical intervals. Modifying the returned array does not mutate this set.

    IpSet::is_empty

    fn IpSet::is_empty(self : IpSet) -> Bool

    Whether the set contains no addresses.

    IpSet::is_subset_of

    fn IpSet::is_subset_of(self : IpSet, other : IpSet) -> Bool

    Whether every address in this set also belongs to other. Scans canonical intervals in O(m+n) time and O(1) extra space, returning immediately when an interval is not covered. A continuous source interval must fit in one canonical covering interval because distinct ones have gaps.

    IpSet::not_equal

    fn IpSet::not_equal(x : IpSet, y : IpSet) -> Bool

    IpSet::overlaps

    fn IpSet::overlaps(self : IpSet, other : IpSet) -> Bool

    Whether the operands share at least one address.

    IpSet::symmetric_difference

    fn IpSet::symmetric_difference(self : IpSet, other : IpSet) -> IpSet

    Return addresses present in exactly one operand.

    IpSet::union

    fn IpSet::union(self : IpSet, other : IpSet) -> IpSet

    Return all addresses present in either operand. Neither operand is mutated. Runs in O(m+n) for m and n canonical intervals.