56ad79ba94
Fixed a couple bugs Added scaffold for extraction
117 lines
3.1 KiB
Zig
117 lines
3.1 KiB
Zig
const std = @import("std");
|
|
const Io = std.Io;
|
|
const File = Io.File;
|
|
const MemoryMap = File.MemoryMap;
|
|
const Atomic = std.atomic.Value;
|
|
|
|
const Decomp = @import("decomp.zig");
|
|
|
|
const DecompCache = @This();
|
|
|
|
alloc: std.mem.Allocator,
|
|
map: MemoryMap,
|
|
decomp_fn: Decomp.Fn,
|
|
|
|
cache: std.AutoHashMap(u64, Cache),
|
|
mut: std.Io.RwLock = .init,
|
|
cond: std.Io.Condition = .init,
|
|
|
|
max_mem: u64,
|
|
cur_mem: u64 = 0,
|
|
|
|
pub fn init(alloc: std.mem.Allocator, map: MemoryMap, compression: Decomp.Enum, max_mem: u64) !DecompCache {
|
|
return .{
|
|
.alloc = alloc,
|
|
.map = map,
|
|
.decomp_fn = try Decomp.DecompFn(compression),
|
|
|
|
.cache = .init(alloc),
|
|
|
|
.max_mem = max_mem,
|
|
};
|
|
}
|
|
pub fn deinit(self: *DecompCache, io: Io) void {
|
|
self.mut.lockUncancelable(io);
|
|
|
|
var iter = self.cache.valueIterator();
|
|
while (iter.next()) |v|
|
|
self.alloc.free(v.data);
|
|
self.cache.deinit();
|
|
}
|
|
|
|
pub fn get(self: *DecompCache, io: Io, offset: u64, compressed_size: u32, max_size: u32) ![]u8 {
|
|
{
|
|
try self.mut.lockShared(io);
|
|
defer self.mut.unlockShared(io);
|
|
|
|
const cache = self.cache.getPtr(offset);
|
|
if (cache != null) {
|
|
_ = cache.?.usage.fetchAdd(1, .acquire);
|
|
return cache.?.data;
|
|
}
|
|
}
|
|
try self.mut.lock(io);
|
|
defer self.mut.unlock(io);
|
|
|
|
const cache = try self.cache.getOrPut(offset);
|
|
if (cache.found_existing) {
|
|
_ = cache.value_ptr.usage.fetchAdd(1, .acquire);
|
|
return cache.value_ptr.data;
|
|
}
|
|
errdefer self.cache.removeByPtr(cache.key_ptr);
|
|
|
|
try self.ensureSpace(io, max_size);
|
|
|
|
var out = try self.alloc.alloc(u8, max_size);
|
|
errdefer self.alloc.free(out);
|
|
|
|
const decomp_size = try self.decomp_fn(self.alloc, self.map.memory[offset..][0..compressed_size], out);
|
|
if (decomp_size != max_size) {
|
|
if (!self.alloc.resize(out, decomp_size)) {
|
|
const new_out = try self.alloc.alloc(u8, decomp_size);
|
|
@memcpy(new_out, out[0..decomp_size]);
|
|
out = new_out;
|
|
} else {
|
|
out.len = decomp_size;
|
|
}
|
|
}
|
|
self.cur_mem += decomp_size;
|
|
|
|
cache.value_ptr.data = out;
|
|
_ = cache.value_ptr.usage.fetchAdd(1, .acquire);
|
|
return out;
|
|
}
|
|
pub fn finished(self: *DecompCache, io: Io, offset: u64) void {
|
|
const cache = self.cache.getPtr(offset);
|
|
if (cache == null) {
|
|
std.debug.print("Finished using cache, but cache does not exist: {}\n", .{offset});
|
|
return;
|
|
}
|
|
const use = cache.?.usage.fetchSub(1, .acquire);
|
|
if (use == 0)
|
|
self.cond.broadcast(io);
|
|
}
|
|
|
|
fn ensureSpace(self: *DecompCache, io: Io, size: u64) !void {
|
|
while (self.cur_mem + size > self.max_mem) {
|
|
var iter = self.cache.valueIterator();
|
|
while (iter.next()) |cache| {
|
|
if (cache.usage.load(.unordered) == 0) {
|
|
self.alloc.free(cache.data);
|
|
self.cur_mem -= cache.data.len;
|
|
|
|
if (self.cur_mem + size <= self.max_mem) return;
|
|
}
|
|
}
|
|
if (self.cur_mem + size <= self.max_mem) return;
|
|
try self.cond.wait(io, &self.mut.mutex);
|
|
}
|
|
}
|
|
|
|
// Types
|
|
|
|
const Cache = struct {
|
|
data: []u8,
|
|
usage: Atomic(u32),
|
|
};
|