further along with popcon
This commit is contained in:
parent
f584642cca
commit
b43ba9c672
121
src/shellpop.zig
121
src/shellpop.zig
|
@ -17,11 +17,10 @@ const ShellIndex = struct {
|
|||
|
||||
// MaxShells is the maximum number of "popular" shells.
|
||||
const MaxShells = 63;
|
||||
const MaxShellLen = 64;
|
||||
|
||||
// ShellPopcon is a shell popularity contest: collect shells and return the
|
||||
// popular ones, sorted by score. score := len(shell) * number_of_shells.
|
||||
// String values are copied, the returned slice of shells is allocated
|
||||
// using an allocator.
|
||||
const ShellPopcon = struct {
|
||||
counts: std.StringHashMap(u32),
|
||||
allocator: Allocator,
|
||||
|
@ -32,20 +31,50 @@ const ShellPopcon = struct {
|
|||
};
|
||||
|
||||
const ShellSections = struct {
|
||||
index: []ShellIndex,
|
||||
blob: []const u8,
|
||||
index: BoundedArray(ShellIndex, MaxShells),
|
||||
blob: BoundedArray(u8, MaxShells * MaxShellLen),
|
||||
|
||||
offsets: StringHashMap(u10),
|
||||
|
||||
pub fn getOffset(self: *ShellSections, shell: []const u8) ?u10 {
|
||||
return self.offsets.get(shell);
|
||||
// initializes and populates shell sections. All strings are copied, nothing is owned.
|
||||
pub fn init(allocator: Allocator, shells: BoundedArray([]const u8, MaxShells)) !ShellSections {
|
||||
var self = ShellSections{
|
||||
.index = try BoundedArray(ShellIndex, MaxShells).init(shells.len),
|
||||
.blob = try BoundedArray(u8, MaxShells * MaxShellLen).init(0),
|
||||
.offsets = StringHashMap(u10).init(allocator),
|
||||
};
|
||||
var offset: u10 = 0;
|
||||
var idx: u10 = 0;
|
||||
while (idx < shells.len) {
|
||||
//const stderr = std.io.getStdErr().writer();
|
||||
//try stderr.print("\n", .{});
|
||||
|
||||
const len = @intCast(u6, shells.get(idx).len);
|
||||
try self.blob.appendSlice(shells.get(idx));
|
||||
const ourShell = self.blob.constSlice()[offset .. offset + len];
|
||||
try self.offsets.put(ourShell, idx);
|
||||
self.index.set(idx, ShellIndex{
|
||||
.offset = offset >> 2, // all shells are padded by 4
|
||||
.len = len,
|
||||
});
|
||||
offset += len;
|
||||
|
||||
// if offset is not multiple by 4, make it so, and append the offset.
|
||||
const padding = (offset + 3) & ~@intCast(u10, 3);
|
||||
offset += padding;
|
||||
try self.blob.appendNTimes(0, padding);
|
||||
idx += 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// initializes ShellSections. All strings are copied, nothing is owned.
|
||||
pub fn init(allocator: Allocator, shells: BoundedArray([]const u8, MaxShells)) ShellSections {
|
||||
self.offsets = StringHashMap(u10).init(allocator);
|
||||
_ = allocator;
|
||||
_ = shells;
|
||||
pub fn deinit(self: *ShellSections) void {
|
||||
self.offsets.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn getOffset(self: *ShellSections, shell: []const u8) ?u10 {
|
||||
return self.offsets.get(shell);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -82,10 +111,10 @@ const ShellPopcon = struct {
|
|||
return std.math.order(a.score, b.score);
|
||||
}
|
||||
|
||||
pub fn getSections(self: *Self, limit: u32) ShellSections {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
_ = stderr;
|
||||
|
||||
// toOwnedSections returns the analyzed ShellSections. Resets the shell
|
||||
// popularity contest. ShellSections memory is allocated by the ShellPopcon
|
||||
// allocator, and must be deInit'ed by the caller.
|
||||
pub fn toOwnedSections(self: *Self, limit: u10) !ShellSections {
|
||||
var deque = PriorityDequeue(KV, void, cmpShells).init(self.allocator, {});
|
||||
defer deque.deinit();
|
||||
|
||||
|
@ -99,55 +128,45 @@ const ShellPopcon = struct {
|
|||
}
|
||||
|
||||
const total = std.math.min(deque.count(), limit);
|
||||
var strSlice = self.allocator.alloc([]u8, total);
|
||||
defer strSlice.deinit();
|
||||
var topShells = try BoundedArray([]const u8, MaxShells).init(total);
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < total) {
|
||||
strSlice[i] = deque.removeMax();
|
||||
const elem: []const u8 = deque.removeMax().shell;
|
||||
topShells.set(i, elem);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return ShellSections.init(self.allocator, strSlice);
|
||||
const result = ShellSections.init(self.allocator, topShells);
|
||||
const allocator = self.allocator;
|
||||
self.* = init(allocator);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
test "[]u8 comparison" {
|
||||
var s1: []const u8 = "/bin/bash";
|
||||
var s2: []const u8 = "/bin/bash";
|
||||
try testing.expectEqual(s1, s2);
|
||||
}
|
||||
|
||||
test "basic shellpop" {
|
||||
test "basic shellpopcon" {
|
||||
var popcon = ShellPopcon.init(testing.allocator);
|
||||
defer popcon.deinit();
|
||||
|
||||
try popcon.put("/bin/bash");
|
||||
try popcon.put("/bin/bash");
|
||||
try popcon.put("/bin/bash");
|
||||
try popcon.put("/bin/zsh");
|
||||
try popcon.put("/bin/zsh");
|
||||
try popcon.put("/bin/zsh");
|
||||
try popcon.put("/bin/zsh");
|
||||
try popcon.put("/bin/nobody");
|
||||
try popcon.put("/bin/very-long-shell-name-ought-to-be-first");
|
||||
try popcon.put("/bin/very-long-shell-name-ought-to-be-first");
|
||||
const bash = "/bin/bash"; // 9 chars
|
||||
const zsh = "/bin/zsh"; // 8 chars
|
||||
const nobody = "/bin/nobody"; // only 1 instance, ought to ignore
|
||||
const long = "/bin/very-long-shell-name-ought-to-be-first";
|
||||
const input = [_][]const u8{
|
||||
zsh, zsh, zsh, zsh, // zsh score 8*4=32
|
||||
bash, bash, bash, nobody, // bash score 3*9=27
|
||||
long, long, // long score 2*42=84
|
||||
};
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
for (input) |shell| {
|
||||
try popcon.put(shell);
|
||||
}
|
||||
|
||||
var topshells = try popcon.top(2);
|
||||
defer topshells.deinit();
|
||||
var shellStrings = topshells.keys();
|
||||
try testing.expectEqual(shellStrings.len, 2);
|
||||
var sections = try popcon.toOwnedSections(MaxShells);
|
||||
defer sections.deinit();
|
||||
try testing.expectEqual(sections.index.len, 3); // all but "nobody" qualify
|
||||
|
||||
try stderr.print("\n", .{});
|
||||
try stderr.print("0th type: {s}\n", .{@typeName(@TypeOf(shellStrings[0]))});
|
||||
try stderr.print("1st type: {s}\n", .{@typeName(@TypeOf(shellStrings[1]))});
|
||||
try stderr.print("0th: {s}, len: {d}\n", .{ shellStrings[0], shellStrings[0].len });
|
||||
try stderr.print("0ww: /bin/very-long-shell-name-ought-to-be-first\n", .{});
|
||||
try stderr.print("1st: {s}, len: {d}\n", .{ shellStrings[1], shellStrings[1].len });
|
||||
try stderr.print("1ww: /bin/zsh\n", .{});
|
||||
|
||||
try testing.expectEqual(shellStrings[0], "/bin/very-long-shell-name-ought-to-be-first");
|
||||
try testing.expectEqual(shellStrings[1], "/bin/zsh");
|
||||
try testing.expectEqual(sections.getOffset(long).?, 0);
|
||||
try testing.expectEqual(sections.getOffset(zsh).?, 1);
|
||||
try testing.expectEqual(sections.getOffset(bash).?, 2);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue