是否有可能在编译时填充大型集合?

6
我们有一个“删除所有我的数据”的功能。我想从许多Web日志文件中删除一组IP。
目前,在运行时,我打开一个带有要删除的IP地址的CSV文件,将其转换为set,扫描文件,并在日志IP匹配时执行删除逻辑。
是否有任何方法可以在编译时加载CSV文件并将其转换为set? 我们正在尝试将东西迁移到AWS Lambda,只需部署一个没有依赖关系的单个静态二进制文件非常方便。
3个回答

9

Rust-PHF 创建的箱子提供编译时数据结构,包括(有序)映射和集合。

不幸的是,到目前为止,它不支持初始化std::net::IpAddr的集合,但可以与静态字符串一起使用:

static IP_SET: phf::Set<&'static str> = phf_set! {
    "127.0.0.1",
    "::1",
};

IPV6不是常量吗?https://doc.rust-lang.org/src/std/net/ip.rs.html#855,由于某些原因,IPV4不稳定... https://doc.rust-lang.org/src/std/net/ip.rs.html#332 - Stargateur
1
@Stargateur,这并不重要,箱子不允许在phf_set!中使用任何表达式,而enum构造函数不是其中允许的之一。我猜它需要在编译时计算哈希值,由于该箱子存在于const之前,因此它只能接受更简单的表达式。 - mcarton
如果这只是IP地址,而不是IP掩码,则u32可能更可取。 - Matthieu M.
@MatthieuM. "如果这只是IPv4" - mcarton
1
你可以尝试使用 newtypeIpAddr 包装起来,并为其实现 PhfHash - Shepmaster
@Shepmaster 如果您使用 phf_codegen,也许可以实现,但如果您使用更简单的 phf_set! 宏,则仅限于简单表达式,不允许使用 enum 构造函数(或任何类似函数调用的东西)。 - mcarton

3

我建议直接使用构建脚本读取CSV文件,并生成一个包含标准HashSet初始值和自定义哈希函数(FxHash等)的源文件。

这样既可以保留编辑CSV文件的方便性,同时仍然将所有数据编译到二进制文件中。它需要一些初始化时间(不像PHF),但指定自定义哈希的能力非常有益。

此外,根据日志中IP的格式,您可能想要存储&'static stru32;后者在搜索方面更有效率,但如果需要转换,则可能会抵消收益。


为什么要使用自定义哈希函数?我不知道它在这里会带来什么好处。 - Shepmaster
实际上,为什么要在运行时创建一个集合呢?在构建脚本中创建一个集合。现在你知道它是唯一的,你可以将所有数据作为一个大数组或切片输出。 - Shepmaster
@Shepmaster:我认为您可能误解了练习的目的。目标不是在csv中打印唯一IP列表(它们很可能已经是唯一的),而是在大量日志文件中进行爬取,查找我们遇到的任何IP地址。因此,目标是优化查找速度,并且具有优化哈希的哈希映射的查找速度比排序数组的查找速度快得多...即使假设使用Eytzinger布局。 - Matthieu M.

3

仅需一个静态二进制文件即可部署

使用include!include_str!将整个CSV文件内联,然后像往常一样继续编写您的程序。

use csv; // 1.0.5

static CSV_FILE: &[u8] = include_bytes!("/etc/hosts");

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut rdr = csv::ReaderBuilder::new()
        .delimiter(b'\t')
        .from_reader(CSV_FILE);

    for result in rdr.records() {
        let record = result?;
        println!("{:?}", record);
    }

    Ok(())
}

另请参阅:


1
使用Rust-PHF mcarton提到的方法,在编译时解析CSV文件是否是可能的? - ForeverConfused
2
@ForeverConfused 这个回答没有什么特别的,但是一般来说。正如Matthieu M.所提到的,您可以使用一个构建脚本来执行CSV本身的读取,然后构造集合。话虽如此,你为什么需要在运行时使用set呢?一旦您确保没有重复项,在大多数情况下数组就已经足够了。 - Shepmaster
对于每个日志,我检查它是否存在于电子邮件集合中,如果找到则将其删除。这种操作在排序集上要快得多。Lambda按调用收费,因此在编译时生成一个集合会带来一些最小的好处。 - ForeverConfused

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接