base/lib.rs
1// base -- portable basics above the runtime and the platform [src/core/str.s, args.s, ini.s]
2//
3// utf8_decode(s, i) -> (codepoint, next) invalid bytes decode as themselves
4// Args: value(name) / flag(name) / int(name, default) over the command line
5// num: f64_parse / f64_format (bit-identical to ASMLIB), parse_i64
6// ini: Ini -- settings files (load / parse / save / get* / set* / remove)
7// settings: Key / Schema / Settings -- typed settings over an Ini (versions, migrations)
8// rng: Pcg32 / SplitMix64 -- deterministic pseudo-random numbers (simulations, tests)
9// metrics: Counter / Gauge / Hist (log-linear) / Window (n slots, quantiles without the
10// values) -- measurements of running systems
11// cmdtree: Tree / Parsed / Help -- IOS-like command trees (abbreviations, ?, Tab, "no", modes)
12// hashring: Ring / hash64 -- consistent hashing with virtual nodes (shards, sticky balancing)
13// lru: Lru -- a map that forgets the least recently used entries (count and weight limits)
14// pattern: glob / has_glob -- shell wildcards; Regex -- POSIX basic and extended regular
15// expressions without back-references (a Pike VM: no exponential cases)
16//
17// Strings and formatting are Rust's own (str, String, format!); the thread pool, cpu
18// features and files are in plat (they need unsafe).
19#![no_std]
20#![forbid(unsafe_code)]
21
22extern crate alloc;
23
24use alloc::string::String;
25use alloc::vec::Vec;
26
27pub mod ini;
28pub mod encoding;
29pub mod xml;
30pub mod num;
31pub mod settings;
32pub mod rng;
33pub mod metrics;
34pub mod cmdtree;
35pub mod hashring;
36pub mod lru;
37pub mod pattern;
38pub use ini::Ini;
39pub use settings::{Key, Schema, Settings};
40pub use num::{f64_format, f64_parse, parse_i64};
41pub use rng::{Pcg32, SplitMix64};
42
43/// UTF-8 decoding: (codepoint, index after it); an invalid / overlong / surrogate / too large
44/// sequence decodes as U+FFFD and advances one byte [src/core/str.s utf8_decode]
45pub fn utf8_decode(s: &[u8], i: usize) -> (u32, usize) {
46 // invalid bytes, overlong forms, surrogates and values above U+10FFFF: U+FFFD, one byte
47 // (as the assembly's utf8_decode; a sequence cut by the end of s is invalid)
48 const BAD: u32 = 0xFFFD;
49 let Some(&b0) = s.get(i) else { return (BAD, i.saturating_add(1)) };
50 let b0 = b0 as u32;
51 if b0 < 0x80 {
52 return (b0, i + 1);
53 }
54 let (n, init, min) = match b0 {
55 0xC2..=0xDF => (1, b0 & 0x1F, 0x80),
56 0xE0..=0xEF => (2, b0 & 0x0F, 0x800),
57 0xF0..=0xF4 => (3, b0 & 7, 0x10000),
58 _ => return (BAD, i + 1), // continuation byte, overlong lead C0 / C1, F5..FF
59 };
60 let mut cp = init;
61 let mut j = i + 1;
62 for _ in 0..n {
63 if j >= s.len() || s[j] & 0xC0 != 0x80 {
64 return (BAD, i + 1);
65 }
66 cp = cp << 6 | (s[j] & 0x3F) as u32;
67 j += 1;
68 }
69 if cp < min || cp > 0x10FFFF || cp & 0xFFFF_F800 == 0xD800 {
70 return (BAD, i + 1); // overlong, too large, surrogate
71 }
72 (cp, j)
73}
74
75/// the command line (argument 0 is the program)
76pub struct Args(pub Vec<String>);
77
78impl Args {
79 /// the argument after `name` (`--shot FILE`); argument 0 (the program) is skipped
80 pub fn value(&self, name: &str) -> Option<&str> {
81 let i = self.0.iter().skip(1).position(|a| a == name)? + 1;
82 self.0.get(i + 1).map(|s| s.as_str())
83 }
84
85 pub fn flag(&self, name: &str) -> bool {
86 self.0.iter().skip(1).any(|a| a == name)
87 }
88
89 /// a decimal value, `def` when missing or invalid
90 pub fn int(&self, name: &str, def: i32) -> i32 {
91 self.value(name).map_or(def, |v| v.parse().unwrap_or(def))
92 }
93}