add some common utilities
This commit is contained in:
parent
ca34198c6d
commit
cc91df4459
|
@ -0,0 +1,104 @@
|
|||
const testing = @import("std").testing;
|
||||
|
||||
pub fn toUpper(x: u8) u8 {
|
||||
var c: i32 = x;
|
||||
const lo: i32 = ('a' - 1) - c; // c >= 'a'
|
||||
const hi: i32 = c - ('z' + 1); // c <= 'z'
|
||||
const mask = (lo & hi) >> 9; // &&
|
||||
c = c + (mask & ('A' - 'a'));
|
||||
return @intCast(u8, c);
|
||||
}
|
||||
|
||||
pub const Hasher32 = Hasher(u32, 16777619, 2166136261);
|
||||
pub const Hasher64 = Hasher(u64, 1099511628211, 14695981039346656037);
|
||||
pub const Hasher128 = Hasher(u128, 309485009821345068724781371, 144066263297769815596495629667062367629);
|
||||
|
||||
pub fn Hasher(comptime T: type, comptime prime: T, comptime bias: T) type {
|
||||
return struct {
|
||||
pub const Prime = prime;
|
||||
pub const Bias = bias;
|
||||
|
||||
pub fn byteIgnoreCase(x: u8, hash: T) T {
|
||||
return (hash ^ toUpper(x)) *% Prime;
|
||||
}
|
||||
|
||||
pub fn byte(x: u8, hash: T) T {
|
||||
return (hash ^ x) *% Prime;
|
||||
}
|
||||
|
||||
pub fn word(x: u16, hash_in: T) T {
|
||||
var hash = (hash_in ^ (0xff & (x >> 0))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 8))) *% Prime;
|
||||
return hash;
|
||||
}
|
||||
|
||||
pub fn dword(x: u32, hash_in: T) T {
|
||||
var hash = (hash_in ^ (0xff & (x >> 0))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 8))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 16))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 24))) *% Prime;
|
||||
return hash;
|
||||
}
|
||||
|
||||
pub fn qword(x: u64, hash_in: T) T {
|
||||
var hash = (hash_in ^ (0xff & (x >> 0))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 8))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 16))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 24))) *% Prime;
|
||||
|
||||
hash = (hash ^ (0xff & (x >> 32))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 40))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 48))) *% Prime;
|
||||
hash = (hash ^ (0xff & (x >> 56))) *% Prime;
|
||||
return hash;
|
||||
}
|
||||
|
||||
pub fn string(str: []const u8, hash_in: T) T {
|
||||
var hash = hash_in;
|
||||
for (str) |c| {
|
||||
hash = byteIgnoreCase(c, hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
pub fn bytes(ptr: *u8, n_bytes: usize, hash: T) T {
|
||||
var i = 0;
|
||||
while (i < n_bytes) : (i += 1) {
|
||||
hash = byte(ptr[i], hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
pub fn hashStr(text: []const u8) T {
|
||||
var hash: T = 0;
|
||||
if (text[0] != 0) {
|
||||
hash = string(text, Bias);
|
||||
hash = if (hash != 0) hash else 1;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
pub fn hashBytes(ptr: *u8, n_bytes: usize) T {
|
||||
var hash: T = 0;
|
||||
if (n_bytes > 0) {
|
||||
hash = bytes(ptr, n_bytes, Bias);
|
||||
hash = if (hash != 0) hash else 1;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "hashing 32-bit" {
|
||||
try testing.expectEqual(@intCast(u32, 0x9de507a8), Hasher32.hashStr("foobar"));
|
||||
try testing.expectEqual(@intCast(u32, 0x9de507a8), Hasher32.hashStr("FOObAR"));
|
||||
}
|
||||
|
||||
test "hashing 64-bit" {
|
||||
try testing.expectEqual(@intCast(u64, 0xed91fadfa4bbf528), Hasher64.hashStr("foobar"));
|
||||
try testing.expectEqual(@intCast(u64, 0xed91fadfa4bbf528), Hasher64.hashStr("FOObAR"));
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
|
||||
const time = @import("time.zig");
|
||||
const Hasher64 = @import("fnv1a.zig").Hasher64;
|
||||
|
||||
// TODO memory
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// eventually the linear allocator will run out
|
||||
const node_limit = 1024;
|
||||
// eventually the call stack will run out in the Gui
|
||||
const depth_limit = 20;
|
||||
|
||||
threadlocal var ctx = Context{};
|
||||
const Context = struct {
|
||||
prevroot: ?*const Node = null,
|
||||
root: Node = Node{},
|
||||
current: ?*Node = null,
|
||||
frame: u64 = 0,
|
||||
count: u64 = 0,
|
||||
depth: i32 = 0,
|
||||
};
|
||||
|
||||
const Node = struct {
|
||||
mark: ?*const ProfileMark = null,
|
||||
parent: ?*Node = null,
|
||||
fchild: ?*Node = null,
|
||||
lchild: ?*Node = null,
|
||||
sibling: ?*const Node = null,
|
||||
begin: time.Instant = undefined,
|
||||
end: time.Instant = undefined,
|
||||
hash: u64 = 0,
|
||||
depth: i32 = 0,
|
||||
};
|
||||
|
||||
pub const ProfileMark = struct {
|
||||
const Self = @This();
|
||||
|
||||
name: []const u8,
|
||||
calls: u32 = 0,
|
||||
sum: u64 = 0,
|
||||
|
||||
pub fn init(name: []const u8) Self {
|
||||
return Self{
|
||||
.name = name,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn begin(self: *const Self) !void {
|
||||
const frame = time.getFrameCount();
|
||||
|
||||
if (frame != ctx.frame) {
|
||||
ctx.current = &ctx.root;
|
||||
|
||||
ctx.frame = frame;
|
||||
ctx.count = 0;
|
||||
ctx.prevroot = &ctx.root;
|
||||
ctx.root = .{ .hash = Hasher64.Bias };
|
||||
ctx.depth = 0;
|
||||
}
|
||||
|
||||
ctx.count += 1;
|
||||
ctx.depth += 1;
|
||||
|
||||
if (ctx.count < node_limit and ctx.depth < depth_limit) {
|
||||
const parent = ctx.current orelse &ctx.root;
|
||||
|
||||
const next = try allocator.create(Node);
|
||||
next.mark = self;
|
||||
next.parent = parent;
|
||||
next.depth = ctx.depth;
|
||||
var prev_hash = parent.hash;
|
||||
const sibling = parent.lchild;
|
||||
if (sibling) |s| {
|
||||
s.sibling = next;
|
||||
prev_hash = s.hash;
|
||||
} else {
|
||||
parent.fchild = next;
|
||||
}
|
||||
|
||||
parent.lchild = next;
|
||||
next.hash = Hasher64.string(self.name, prev_hash) | 1;
|
||||
ctx.current = next;
|
||||
next.begin = time.now();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(self: *const Self) void {
|
||||
const now = time.now();
|
||||
|
||||
if (ctx.count < node_limit and ctx.depth < depth_limit) {
|
||||
const cur = ctx.current orelse unreachable;
|
||||
|
||||
assert(cur.mark == self);
|
||||
assert(cur.depth == ctx.depth);
|
||||
assert(cur.parent orelse null != null);
|
||||
assert(cur.hash != 0);
|
||||
|
||||
cur.end = now;
|
||||
|
||||
ctx.current = cur.parent;
|
||||
}
|
||||
|
||||
ctx.depth -= 1;
|
||||
}
|
||||
|
||||
// TODO gui shit
|
||||
};
|
||||
|
||||
const Testing = struct {
|
||||
const pm_slow_func = ProfileMark.init("Test.slow_func");
|
||||
fn slow_func() !void {
|
||||
try pm_slow_func.begin();
|
||||
defer pm_slow_func.end();
|
||||
|
||||
std.time.sleep(100);
|
||||
}
|
||||
};
|
||||
|
||||
test "profile test" {
|
||||
try Testing.slow_func();
|
||||
const fchild = ctx.root.fchild orelse unreachable;
|
||||
|
||||
try testing.expectEqual(std.math.Order.lt, fchild.begin.order(fchild.end));
|
||||
try testing.expect(fchild.end.since(fchild.begin) >= 100);
|
||||
try testing.expectEqual(Hasher64.string("test.slow_func", 0) | 1, fchild.hash);
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
const std = @import("std");
|
||||
const time = std.time;
|
||||
const testing = std.testing;
|
||||
|
||||
pub fn lerp(comptime T: type, a: T, b: T, t: T) T {
|
||||
return (a + (b - a)) * t;
|
||||
}
|
||||
|
||||
pub const Instant = time.Instant;
|
||||
|
||||
var m_app_start: time.Instant = undefined;
|
||||
var m_update: time.Instant = undefined;
|
||||
var m_prev_update: time.Instant = undefined;
|
||||
var m_dt: u64 = undefined;
|
||||
var m_dtf: f32 = undefined;
|
||||
var m_smooth_dtf: f32 = undefined;
|
||||
var m_frame_count: u32 = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
m_frame_count = 0;
|
||||
m_app_start = now();
|
||||
m_update = m_app_start;
|
||||
}
|
||||
|
||||
pub fn update() void {
|
||||
m_frame_count += 1;
|
||||
const now_time = now();
|
||||
m_dt = now_time.since(m_prev_update);
|
||||
m_prev_update = m_update;
|
||||
m_update = now_time;
|
||||
m_dtf = @intToFloat(f32, m_dt) / time.ms_per_s;
|
||||
m_smooth_dtf = lerp(f32, m_smooth_dtf, m_dtf, (1.0 / 120.0));
|
||||
}
|
||||
|
||||
pub fn deinit() void {}
|
||||
|
||||
pub fn now() time.Instant {
|
||||
return time.Instant.now() catch unreachable;
|
||||
}
|
||||
|
||||
pub fn lap(tick: *time.Instant) u64 {
|
||||
const prev = tick;
|
||||
tick.* = now();
|
||||
return prev;
|
||||
}
|
||||
|
||||
pub fn getFrameCount() u64 {
|
||||
return m_frame_count;
|
||||
}
|
||||
pub fn getAppStart() u64 {
|
||||
return m_app_start;
|
||||
}
|
||||
pub fn getPrevFrame() time.Instant {
|
||||
return m_prev_update;
|
||||
}
|
||||
pub fn getDelta() u64 {
|
||||
return m_dt;
|
||||
}
|
||||
pub fn deltaf() f32 {
|
||||
return m_dtf;
|
||||
}
|
||||
pub fn smoothDeltaf() f32 {
|
||||
return m_smooth_dtf;
|
||||
}
|
||||
|
||||
test "time tests" {
|
||||
init();
|
||||
defer deinit();
|
||||
|
||||
time.sleep(10);
|
||||
|
||||
update();
|
||||
|
||||
try testing.expect(m_app_start.order(m_update) == .lt);
|
||||
|
||||
time.sleep(10);
|
||||
update();
|
||||
|
||||
try testing.expect(m_app_start.order(m_update) == .lt);
|
||||
try testing.expect(m_prev_update.order(m_update) == .lt);
|
||||
}
|
Loading…
Reference in New Issue