use bytes; use fmt; use io; use os; use sys; use sort; use sort::cmp; use strings; use types; def JOBS: size = 4; type stat = struct { min: i64, max: i64, sum: i64, count: size }; type thread_job = struct { chunk: []u8, map: hashmap, names_buf: [10000](size, str), names: [](size, str) }; const jobs: [JOBS]thread_job = [thread_job { ... }...]; fn i64parse(bytes: []u8) (i64, size) = { let index = 0z; let u: i64 = 0; let neg = 1; if (bytes[index] == '-') { neg = -1; index += 1; }; u = (bytes[index] - '0'): i64; index += 1; if (bytes[index] != '.') { u = u * 10 + (bytes[index] - '0'): i64; index += 1; }; index += 1; u = u * 10 + (bytes[index] - '0'): i64; index += 2; return (u * neg, index); }; @test fn parsei64() void = { assert(i64parse(strings::toutf8("0.0")).0 == 0); assert(i64parse(strings::toutf8("9.2")).0 == 92); assert(i64parse(strings::toutf8("-9.2")).0 == -92); assert(i64parse(strings::toutf8("98.2")).0 == 982); assert(i64parse(strings::toutf8("-98.2")).0 == -982); }; fn work(arg: *opaque) nullable *opaque = { let job = arg: *thread_job; for (true) { let hash = 14695981039346656037z; let sep_idx = 0z; let has_semi = false; for (const char .. job.chunk) { if (char == ';') { has_semi = true; break; }; hash ^= char; hash *= 1099511628211; sep_idx += 1; }; if (!has_semi) break; let after = job.chunk[sep_idx + 1..]; const name = strings::fromutf8_unsafe(job.chunk[..sep_idx]); const (temp, index) = i64parse(after); job.chunk = after[index..]; let stat = getitem(&job.map, hash); if (stat.count == 0) { static append(job.names, (hash, strings::dup(name)!))!; stat.min = temp; stat.max = temp; } else { if (stat.min > temp) stat.min = temp; if (stat.max < temp) stat.max = temp; }; stat.sum += temp; stat.count += 1; setitem(&job.map, hash, stat); }; return null; }; fn mmap(fd: io::file) []u8 = { let st = sys::st { ... }; sys::fstat(fd, &st)!; let ptr = sys::mmap(null, st.sz, sys::PROT_READ, sys::MAP_PRIVATE, fd, 0)!; // Hardcoded value for MADV_SEQUENTIAL, from libc-rs it seems like it's // 2 on all currently supported platforms (Linux, FreeBSD, NetBSD, // OpenBSD, Dragonfly). assert(sys::syscall(sys::SYS_madvise, ptr: uintptr: u64, st.sz: u64, 2) == 0); return *(&types::slice { data = ptr, length = st.sz, capacity = 0 }: *[]u8); }; fn munmap(map: []u8) void = { let map = &map: *types::slice; sys::munmap(map.data: *opaque, map.length)!; }; fn names_cmp(a: *opaque, b: *opaque) int = { const a = *(a: *(size, str)); const b = *(b: *(size, str)); return cmp::strs(&a.1, &b.1); }; export fn main() void = { if (len(os::args) > 2) fmt::fatalf("usage: {} ", os::args[0]); let file = "measurements.txt"; if (len(os::args) == 2) file = os::args[1]; const handle = os::open(file)!; defer io::close(handle)!; const map = mmap(handle); defer munmap(map); const chunk_size = len(map) / JOBS; let threads: [JOBS]pthread_t = [0...]; let at = 0z; for (let i = 0z; i < JOBS; i += 1) { const start = at; let end = at + chunk_size; if (end < len(map)) { let newline_at = match(bytes::index(map[end..], '\n')) { case let idx: size => yield idx; case void => abort(); }; end = end + newline_at + 1; } else { end = len(map); }; jobs[i].chunk = map[start..end]; jobs[i].names = jobs[i].names_buf[..0]; pthread_create(&threads[i], null, &work, &jobs[i])!; at = end; }; for (const thread .. threads) pthread_join(thread, null)!; let names_buf: [10000](size, str) = [(0, "")...]; let names = names_buf[..0]; let stats: hashmap = [(0, stat { ... })...]; for (let job &.. jobs) { for (let (hash, name) .. job.names) { let stat = getitem(&stats, hash); let new_stat = getitem(&job.map, hash); if (stat.count == 0) { static append(names, (hash, strings::dup(name)!))!; stat.min = new_stat.min; stat.max = new_stat.max; } else { if (stat.min > new_stat.min) stat.min = new_stat.min; if (stat.max < new_stat.max) stat.max = new_stat.max; }; stat.sum += new_stat.sum; stat.count += new_stat.count; setitem(&stats, hash, stat); }; }; sort::sort(names, size((size, str)), &names_cmp)!; fmt::print("{")!; for (let i = 0z; i < len(names); i += 1) { let station = names[i].1; let item = getitem(&stats, names[i].0); if (i > 0) fmt::print(", ")!; fmt::printf("{}={:.1f}/{:.1f}/{:.1f}", station, item.min: f64 / 10.0, item.sum: f64 / 10.0 / item.count: f64, item.max: f64 / 10.0, )!; free(names[i].1); }; fmt::println("}")!; };