在ATS中正确实现getenv()的技术方案探究
先聊聊背景:在Linux系统中,argv和envp的类型完全一致——都是指向以NULL结尾的指针数组的指针,数组里的每个元素又指向以NUL结尾的字符串。而且这些字符串的内存在进程生命周期内持续有效,完全不用我们操心内存管理的问题。
envp里的字符串格式固定为KEY=VALUE,系统自带的getenv("KEY")本质就是遍历envp中的字符串,把每个字符串的前缀和KEY对比,找到匹配项后返回=后面的字符串指针。这种查找方式不需要分配内存,但速度肯定比哈希表查找慢。另外要注意,系统只保证envp的类型合法性,不会提供数组的具体大小信息。
我想实现的类型安全getenv_from
我希望编写一个适配这种内存布局的getenv(),返回指向目标字符串的指针,尽可能减少内存分配。预期的实现逻辑大概是这样的:
fun getenv_from(key: string, env: magic): [b:bool] option_vt(string, b) = if string2ptr(ptr[0]) > the_null_pointer then ( case+ string_is_prefix(string_append(key, "="), env[0]) of | true => Some_vt(string_make_suffix(env[0], string_length(key)+1)) | false => getenv_from(key, succ(env)) ) else None_vt()
这里的magic类型应该是带praxi证明断言的envp指针,用来告诉ATS这个类型的特性,让它能正确处理内存访问。
之前的变通方案(有明显缺陷)
我之前试过一种取巧的方法,把envp伪装成argv类型来处理:
#include "share/atspre_staload.hats" extern fun fake_free{n:int}: argv(n) -> void = "mac#" %{ void fake_free(void *arg) {} %} // print out the first environment variable, as an example implement main{n:int}(argc, argv, envp) = let val env2 = $UNSAFE.castvwtp1{argv(n)}(envp) in if argc > 0 then ( print_string(env2[0]); print_newline(); fake_free(env2); 0 ) end
这个方案虽然能跑,但有两个明显的问题:一是要处理和envp完全无关的虚假argc约束,二是还要应对不必要的线性类型问题,用起来很别扭。
核心诉求
这里要明确:从实用角度来说,在ATS里直接调用C的getenv(),或者用ATS库自带的getenv_gc、getenv_opt、getenv_exn'这些方法都很简单,但我的问题不是“怎么在ATS里获取环境变量”,而是**“怎么在ATS里正确实现一个类型安全的getenv()”**——给定一个裸指针,我要怎么通过ATS的类型系统告知它这个指针的特性,让操作合法的同时,避免使用$UNSAFE.ptr_xxx这类C风格的调用,保留ATS可证明的安全内存访问优势。我查过ATS库,觉得parray_v可能有用,但没找到相关的使用示例。
内容的提问来源于stack exchange,提问作者Julian Fondren

