公告

FujoOS公布日延期,以仓库发布为准

文档 语言 官方库 · std 与 host

官方库 · std 与 host

先说清一件事:Loment 语言本身没有标准库。语言里只有内建函数(alloc / load8 / str_* / syscall4 那一组,二十来个),没有 String、没有 Vec、没有 HashMap。下面这些是一个库——和你能写的那个库是同一种东西,只是它是官方维护的。

所以它没有特权:依赖一样从源码的 use 来,身份一样是内容哈希,选版一样只有「版本最大」一条规则。它是库系统的第一个大用户,也是Lompi 的第一个大输入。

127 std 里的模块(外加一个包入口,共 128 个文件)
0 std 里发出去的 syscall 数量
2 包:可移植的 std,与带 syscall 的 host
1 依赖方向:host → std → numfmt(单向)

127 个模块这个数不是估的:一个包入口 std.lomt,里面 127 条 use,加上它自己就是 128 个文件。

为什么是两个包,不是一个

内容 发不发 syscall
std · 0.1.0 127 个模块:数值、容器、文本、摘要、压缩、编解码、图像、时间 一个都不发
host · 0.1.0 7 个模块:argv · dir · fs · io · log · stat · 包入口 发(getdents64 / openat / newfstatat 这类)

依赖方向是单向的:host → std → numfmt,反不过来。这条方向是刻意的,不是碰巧——裸机、无宿主、或者只想要一段 CRC 的消费者,只依赖 std,不会把 getdents64 和目录遍历一起拖进二进制。基础内建之外最底下的那一层是 numfmt(整数转文本),它上面才长别的。

一句话:把「需要操作系统的」和「不需要的」分成两个包,是为了让后者能去没有操作系统的地方。

怎么用它

消费方在源码里写依赖——没有别的清单要维护。库里公开的名字带前缀math_gcd / crc32_str / out_str),因为发射出来的符号名是平的,一个单元里顶层名字必须唯一。

loment 一个消费方
module consumer

use std

fn _start() {
    let b: ptr = alloc(256);
    let o: u32 = 0;
    o = out_str(b, o, "gcd=");
    o = out_dec(b, o, math_gcd(12, 18) as u64);
    ...
}
Pre1 修了一半,另一半没修——实测出来的: 原先打包版有两个限制:单文件最多 8 条 use,以及名字形式的搜索只认仓库相对的四个根——于是装在用户机器上(没有仓库)时 use std 一个都找不到。Pre1 把这两条都改了:条数上限提到 300(超了报 E020),搜索改成三层(① <项目根>/deps/ ② 工具链自带的 store ③ 内置四根)。

use std 仍然编不过。拿装好的 Pre2 实测:它找得到 std(名字写错是立刻报「找不到或有歧义」的,写 std 不报),然后真的把整个包装进来——耗时 9 分 4 秒(同一台机器上一个普通文件的 check 是 0.088 秒)——最后以一片错误收场:合并出来的单元里有重复的顶层名字(E13std 的模块共用 mem / wide,就是菱形),另有一批元数与类型不符(E3 / E1)。

所以结论分两半:找得到是 Pre1 修好的;合得起来不是——那一条还在。实际用法照旧:按需 use 具体模块;要整体消费就交给 python tools/loment.py lib materialize,它走去重加物化,而不是把 127 个模块硬拼成一个单元。

模块索引

下面每一行的说明都来自那个模块自己文件头的第一行,不是概括出来的。

数值:整数、浮点、大数

math整数数学
intmath整数数论(gcd / lcm / isqrt / powmod / invmod / 素性 / 试除分解)
num整数打包、上下界与夹取
numfmt纯数字格式化(整数 → 文本)——整棵树最底下的一层
round取整语义(浮点四种模式 + 整数除法 / 倍数)
comb组合数学(阶乘 / 二项式 / 排列 / 卡特兰 / 斯特林)
f64双精度浮点算术(IEEE 754,软实现)
f64bitsIEEE 754 双精度的位级视图
f64conv整数 ↔ 浮点,以及单精度 ↔ 双精度
f64fmt浮点的十进制文本(dtoa)
mathf浮点超越函数(sqrt / exp / log / 三角)
fixed定点小数(i64,六位小数)
rational精确分数(有理数)算术
bigint任意精度无符号整数
bigdec十进制定点大数(系数 + 标度)
wide64 位之上的算术(128 位积、128/64 除、跨字移位、位计数)
matrix稠密矩阵(i64,行主序)
stats基本统计量(均值 / 方差 / 中位数 / 分位数 / 直方图 / 相关系数)
noise定点值噪声 / Perlin 噪声 / 分形叠加(fbm)
interp定点插值与缓动(t 是千分比 0..1024)
rand确定性伪随机(xorshift)
rng确定性伪随机(splitmix64 播种 + xoshiro256** 生成 + 无偏取模 + 洗牌)

内存、缓冲与容器

mem多字节读写与块操作
endian字节序读写与翻转
bit位操作
atomics原子操作:有什么、缺什么、缺的怎么替
arena基于调用方缓冲的 bump 分配器
mall空闲块分配器(在调用方给的 arena 上)
pool定长对象池(slab)
memdbg分配记账与哨兵
buf有容量检查的字节构建器
vecu32 动态数组(调用方持有缓冲)
deque双端队列(环形缓冲)
ring环形缓冲(FIFO,调用方持有数组)
ilist侵入式双向链表(节点池在调用方的缓冲里)
map开放寻址哈希表(u64 → u64)
mapstr字符串键哈希表(字节串 → u64)
set哈希集合(u64)
lruLRU 缓存(开放寻址哈希 + 双向链表,节点用下标不用指针)
trie前缀树(字节串键)
bst二叉搜索树(有序映射,节点池在调用方的缓冲里)
heap二叉最小堆(优先队列)
bitset位图(调用方持有字节缓冲)
flags位标志集合(通用小位集工具)
bloom布隆过滤器(位数组 + k 个哈希)
intern字符串驻留(string interning)
unionfind并查集(不相交集合;按秩合并 + 路径压缩)
fenwick树状数组 / BIT(单点加 + 前缀和)
graph图的基础表示与遍历
shortest最短路:Dijkstra / Bellman-Ford / Floyd-Warshall
mst最小生成树:Kruskal 与 Prim

文本与字符串

utf8UTF-8 编解码与码点计数
char字节分类与大小写
caseUnicode 简单大小写映射与不区分大小写比较
text字节段的比较与查找
split按分隔符切分
trim去空白
quote转义与反转义
line按行遍历
bufio缓冲读:行切分 + 环形缓冲
parse文本 → 整数
path路径切分
globglob 通配匹配(* ? [...] \),含路径语义
search文本搜索:朴素 / KMP / Boyer-Moore-Horspool
hash非加密哈希
hashx非加密哈希(FNV-1a / xxHash32)+ 一致性哈希环
fmt对齐与填充
table对齐表格渲染(列宽 + 填充 + 分隔线)
width终端里的显示宽度(东亚宽度)
wrap按显示宽度折行(word wrap)
word排版语义上的「词」(word segmentation)
sortu32 数组排序(原地)
bsearch已排序 u32 数组上的二分
udiff行级差异(LCS + 统一 diff)

摘要、校验与压缩

sha256SHA-256(FIPS 180-4)
sha1SHA-1(FIPS 180-4)
sha512SHA-512(FIPS 180-4)
md5MD5(RFC 1321)
hmacHMAC(RFC 2104)
kdf密钥派生(PBKDF2 与 HKDF,都基于 HMAC-SHA256)
crc32CRC-32(IEEE 802.3,反射多项式 0xEDB88320)
crc32cCRC-32 的几种参数
crc16CRC-16 的几种常见参数
adler32Adler-32
sum加法族校验和(Fletcher / 反码和 / 字节和 / 异或)
deflateDEFLATE 压缩(RFC 1951)
inflateDEFLATE 解压(RFC 1951)
zlibczlib(RFC 1950)容器:头 + DEFLATE 数据 + 大端 Adler-32
lzssLZSS(滑动窗口 + 长度 / 距离对)
huffman霍夫曼编码:频率表 → 码长 → 规范码
rle行程编码(RLE)的两种常见形态

编解码与格式(全部游标式读取)

这一组有一个共同点:值留在原文里,不建树。读取是游标式的——你拿着原文和一个位置往前走,而不是先把整个文档变成一个对象图。没有 DOM,因为建 DOM 要分配,而这个语言的空间是调用方给的。

base64Base64(标准字母表,带 = 填充)
base32Base32(RFC 4648,字母表 A-Z2-7)
base58Base58(Bitcoin 字母表)
base85Base85(Z85 字母表,4 字节 → 5 字符)
hex十六进制编解码与转储
hexdump十六进制转储(hexdump -C / xxd 风格)
urlURL 百分号编码 / 解码 + 查询串取值
varintLEB128 变长整数 + zigzag
jsnJSON 读取(游标式,不建 DOM)
cborCBOR 读取(RFC 8949,游标式)
mpkMessagePack 读取(游标式)
asn1ASN.1 DER 读取(游标式)
tomlTOML 读取(游标式)
iniINI / 键值配置文件
csvCSV 字段的读写(RFC 4180 的那套引号规则)
ansiANSI 转义序列(SGR / 光标 / 屏幕)+ 流解析

图像与终端

raster像素缓冲:填充 / 矩形 / 直线 / 圆
blit位块传送:拷贝 / alpha 混合 / 缩放 / 着色 / 颜色键
font8×8 点阵字体与文字绘制
bmpBMP 读写(只做未压缩 24/32 位)
pngPNG 容器:签名 / 块遍历 / IHDR / 扫描线反滤波
qoiQOI 图像格式(Quite Ok Image,规范 v1.0)
tui终端界面:单元格网格 + 差异渲染 + 画框

时间、系统与其他

civil公历日期 / 时间的纯整数换算
clock时间的文本表示与时长算术
assert断言与定位
out游标式输出层(可移植层)
once一次性初始化(赢家选举 + 三态迁移)
spinticket 锁 / 有界自旋等待 / 计数信号量 / latch
coro可恢复的状态机(不是有独立栈的协程)
sched数据驱动的合作式调度表(不是抢占式调度器)
perm权限位 ↔ rwxr-xr-x 文本
proc/proc/self/* 的解析
env环境块 / argv 的解析(\0 分隔的连续字节区)
fwalklinux_dirent64 记录的解析与路径拼接
tmpf临时文件名的生成(没有 mkdir/unlink/getpid 时怎么不撞名)
std包入口(把 127 个模块拉进同一个单元)

宿主层 · host

ioLoment 的第一个库:宿主侧 IO 与字节原语
fs整文件读写(在 io 之上)
argv进程启动参数
dir目录遍历(getdents64)
stat文件元信息(newfstatat)
log工具链那套诊断行的约定

几条贯穿全库的约定

  • 公开名字带模块前缀 —— hex_*crc32_*math_*out_*。因为发射的符号名是平的、一个单元里顶层名字必须唯一,前缀是这条约束的纪律,不是命名癖好。
  • 容器不收自己的账 —— vec / arena / bst / pool / ring / bitset 一律由调用方持有缓冲。bump 堆只有 64 KiB,库自己分配会把上限摊到每一个使用者身上。
  • 格式读取是游标式的 —— jsn / cbor / mpk / asn1 / toml 都不建树。同一个理由:建树要分配。
  • 先定义后使用 —— 语言要求如此。hex.lomthex_val 排在 hex_decode 前面,那不是风格,是编译器会拦。
  • 名字里有「不是」的东西会写明 —— coro 的文件头第一句就是「不是有独立栈的协程」,sched 是「不是抢占式调度器」。免得有人按名字想当然。
想找某一个函数: 每个模块的文件头写着它的约定与命名,pub fn 上面那行 /// 是文档注释,loment doc FILE 会把它们抽出来。写一个 Loment 库 讲的是一个库该怎么组织,Lompi 讲的是怎么把它解析、发锁、物化。