1
0
Fork 0

Fix json output with invalid unicode.

This commit is contained in:
namedkitten 2020-08-09 13:16:33 +01:00
parent b7f616a945
commit 419b99c277
9 changed files with 2170 additions and 54 deletions

View file

@ -34,11 +34,6 @@ pub fn build(b: *Builder) void {
"weather_location", "weather_location",
"weather_location", "weather_location",
) orelse ""; ) orelse "";
if (weather_location.len == 0) {
weather_location = "\"\"";
} else if (weather_location[0] != '"') {
weather_location = std.fmt.allocPrint(b.allocator, "\"{}\"", .{weather_location}) catch "\"\"";
}
exe.addBuildOption([]const u8, "weather_location", weather_location); exe.addBuildOption([]const u8, "weather_location", weather_location);
//exe.strip = true; //exe.strip = true;

67
build_runner.zig Normal file
View file

@ -0,0 +1,67 @@
const root = @import("build.zig");
const std = @import("std");
const io = std.io;
const fmt = std.fmt;
const Builder = std.build.Builder;
const Pkg = std.build.Pkg;
const InstallArtifactStep = std.build.InstallArtifactStep;
const LibExeObjStep = std.build.LibExeObjStep;
const ArrayList = std.ArrayList;
///! This is a modified build runner to extract information out of build.zig
///! Modified from the std.special.build_runner
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
const builder = try Builder.create(allocator, "", "", "");
defer builder.destroy();
try runBuild(builder);
const stdout_stream = io.getStdOut().outStream();
// TODO: We currently add packages from every LibExeObj step that the install step depends on.
// Should we error out or keep one step or something similar?
// We also flatten them, we should probably keep the nested structure.
for (builder.top_level_steps.items) |tls| {
for (tls.step.dependencies.items) |step| {
try processStep(stdout_stream, step);
}
}
}
fn processStep(stdout_stream: anytype, step: *std.build.Step) anyerror!void {
if (step.cast(InstallArtifactStep)) |install_exe| {
for (install_exe.artifact.packages.items) |pkg| {
try processPackage(stdout_stream, pkg);
}
} else if (step.cast(LibExeObjStep)) |exe| {
for (exe.packages.items) |pkg| {
try processPackage(stdout_stream, pkg);
}
} else {
for (step.dependencies.items) |unknown_step| {
try processStep(stdout_stream, unknown_step);
}
}
}
fn processPackage(out_stream: anytype, pkg: Pkg) anyerror!void {
try out_stream.print("{}\x00{}\n", .{ pkg.name, pkg.path });
if (pkg.dependencies) |dependencies| {
for (dependencies) |dep| {
try processPackage(out_stream, dep);
}
}
}
fn runBuild(builder: *Builder) anyerror!void {
switch (@typeInfo(@TypeOf(root.build).ReturnType)) {
.Void => root.build(builder),
.ErrorUnion => try root.build(builder),
else => @compileError("expected return type of build to be 'void' or '!void'"),
}
}

View file

@ -7,6 +7,7 @@ const log = std.log;
const terminal_version = @import("build_options").terminal_version; const terminal_version = @import("build_options").terminal_version;
const debug_allocator = @import("build_options").debug_allocator; const debug_allocator = @import("build_options").debug_allocator;
const disable_terminal_mouse = @import("build_options").disable_terminal_mouse; const disable_terminal_mouse = @import("build_options").disable_terminal_mouse;
const json = @import("json.zig");
fn readFromSignalFd(signal_fd: std.os.fd_t) !void { fn readFromSignalFd(signal_fd: std.os.fd_t) !void {
var buf: [@sizeOf(os.linux.signalfd_siginfo)]u8 align(8) = undefined; var buf: [@sizeOf(os.linux.signalfd_siginfo)]u8 align(8) = undefined;
@ -14,31 +15,6 @@ fn readFromSignalFd(signal_fd: std.os.fd_t) !void {
return error.Shutdown; return error.Shutdown;
} }
pub fn utf8ValidateSlice(s: []const u8) bool {
var i: usize = 0;
while (i < s.len) {
if (std.unicode.utf8ByteSequenceLength(s[i])) |cp_len| {
if (i + cp_len > s.len) {
log.err(.uni, "oh nos: {} {} {}\n", .{i + cp_len, i, s.len});
log.err(.uni, "oh no: {}\n", .{s[i .. i + cp_len]});
return false;
}
if (std.unicode.utf8Decode(s[i .. i + cp_len])) |_| {} else |_| {
log.err(.uni, "oh no: {}\n", .{s[i .. i + cp_len]});
return false;
}
i += cp_len;
} else |err| {
log.err(.uni, "oh noz: {} {} {} {} \"{}\" \n", .{s[i], s.len, i, err, s[0..i+1]});
return false;
}
}
return true;
}
fn sigemptyset(set: *std.os.sigset_t) void { fn sigemptyset(set: *std.os.sigset_t) void {
for (set) |*val| { for (set) |*val| {
val.* = 0; val.* = 0;
@ -103,7 +79,7 @@ pub const Bar = struct {
// Serialize all bar items and put on stdout. // Serialize all bar items and put on stdout.
try self.out_file.writer().writeAll("["); try self.out_file.writer().writeAll("[");
for (self.infos.items) |info, i| { for (self.infos.items) |info, i| {
try std.json.stringify(info, .{}, self.out_file.writer()); try json.stringify(info, .{}, self.out_file.writer());
if (i < self.infos.items.len - 1) { if (i < self.infos.items.len - 1) {
try self.out_file.writer().writeAll(","); try self.out_file.writer().writeAll(",");
@ -239,9 +215,9 @@ pub const Bar = struct {
// instead of looping and getting it, maybe then it would make more sense? // instead of looping and getting it, maybe then it would make more sense?
// Anyway this just strips off the prefix of ',' so I can parse the json. // Anyway this just strips off the prefix of ',' so I can parse the json.
if (line[0] == ',') line = line[1..line.len]; if (line[0] == ',') line = line[1..line.len];
const parseOptions = std.json.ParseOptions{ .allocator = self.allocator }; const parseOptions = json.ParseOptions{ .allocator = self.allocator };
const data = try std.json.parse(MouseEvent, &std.json.TokenStream.init(line), parseOptions); const data = try json.parse(MouseEvent, &json.TokenStream.init(line), parseOptions);
defer std.json.parseFree(MouseEvent, data, parseOptions); defer json.parseFree(MouseEvent, data, parseOptions);
self.dispatch_click_event(data.name, data) catch {}; self.dispatch_click_event(data.name, data) catch {};
// If mouse_event needs to store the event for after the call is finished, // If mouse_event needs to store the event for after the call is finished,
@ -316,9 +292,6 @@ pub const Bar = struct {
} }
// If we reach here then it changed. // If we reach here then it changed.
try self.free_info(infoItem); try self.free_info(infoItem);
if (!utf8ValidateSlice(info.full_text)) {
std.log.err(.barerr, "Oh No: {}\n", .{info.full_text});
}
self.infos.items[index] = try self.dupe_info(info); self.infos.items[index] = try self.dupe_info(info);
try self.print_infos(false); try self.print_infos(false);
} }
@ -332,7 +305,7 @@ pub fn initBar(allocator: *std.mem.Allocator) Bar {
.widgets = undefined, .widgets = undefined,
.running = false, .running = false,
.infos = std.ArrayList(Info).init(allocator), .infos = std.ArrayList(Info).init(allocator),
.items_mutex = std.Mutex.init(), .items_mutex = std.Mutex{},
.out_file = std.io.getStdOut(), .out_file = std.io.getStdOut(),
}; };
} }

1810
src/bar/json.zig Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,250 @@
const std = @import("../std.zig");
const assert = std.debug.assert;
const maxInt = std.math.maxInt;
const State = enum {
Complete,
Value,
ArrayStart,
Array,
ObjectStart,
Object,
};
pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
return struct {
const Self = @This();
pub const Stream = OutStream;
whitespace: std.json.StringifyOptions.Whitespace = std.json.StringifyOptions.Whitespace{
.indent_level = 0,
.indent = .{ .Space = 1 },
},
stream: OutStream,
state_index: usize,
state: [max_depth]State,
pub fn init(stream: OutStream) Self {
var self = Self{
.stream = stream,
.state_index = 1,
.state = undefined,
};
self.state[0] = .Complete;
self.state[1] = .Value;
return self;
}
pub fn beginArray(self: *Self) !void {
try self.stream.writeByte('[');
self.state[self.state_index] = State.ArrayStart;
self.whitespace.indent_level += 1;
}
pub fn beginObject(self: *Self) !void {
try self.stream.writeByte('{');
self.state[self.state_index] = State.ObjectStart;
self.whitespace.indent_level += 1;
}
pub fn arrayElem(self: *Self) !void {
const state = self.state[self.state_index];
switch (state) {
.Complete => unreachable,
.Value => unreachable,
.ObjectStart => unreachable,
.Object => unreachable,
.Array, .ArrayStart => {
if (state == .Array) {
try self.stream.writeByte(',');
}
self.state[self.state_index] = .Array;
self.pushState(.Value);
try self.indent();
},
}
}
pub fn objectField(self: *Self, name: []const u8) !void {
const state = self.state[self.state_index];
switch (state) {
.Complete => unreachable,
.Value => unreachable,
.ArrayStart => unreachable,
.Array => unreachable,
.Object, .ObjectStart => {
if (state == .Object) {
try self.stream.writeByte(',');
}
self.state[self.state_index] = .Object;
self.pushState(.Value);
try self.indent();
try self.writeEscapedString(name);
try self.stream.writeByte(':');
if (self.whitespace.separator) {
try self.stream.writeByte(' ');
}
},
}
}
pub fn endArray(self: *Self) !void {
switch (self.state[self.state_index]) {
.Complete => unreachable,
.Value => unreachable,
.ObjectStart => unreachable,
.Object => unreachable,
.ArrayStart => {
self.whitespace.indent_level -= 1;
try self.stream.writeByte(']');
self.popState();
},
.Array => {
self.whitespace.indent_level -= 1;
try self.indent();
self.popState();
try self.stream.writeByte(']');
},
}
}
pub fn endObject(self: *Self) !void {
switch (self.state[self.state_index]) {
.Complete => unreachable,
.Value => unreachable,
.ArrayStart => unreachable,
.Array => unreachable,
.ObjectStart => {
self.whitespace.indent_level -= 1;
try self.stream.writeByte('}');
self.popState();
},
.Object => {
self.whitespace.indent_level -= 1;
try self.indent();
self.popState();
try self.stream.writeByte('}');
},
}
}
pub fn emitNull(self: *Self) !void {
assert(self.state[self.state_index] == State.Value);
try self.stringify(null);
self.popState();
}
pub fn emitBool(self: *Self, value: bool) !void {
assert(self.state[self.state_index] == State.Value);
try self.stringify(value);
self.popState();
}
pub fn emitNumber(
self: *Self,
value: anytype,
) !void {
assert(self.state[self.state_index] == State.Value);
switch (@typeInfo(@TypeOf(value))) {
.Int => |info| {
if (info.bits < 53) {
try self.stream.print("{}", .{value});
self.popState();
return;
}
if (value < 4503599627370496 and (!info.is_signed or value > -4503599627370496)) {
try self.stream.print("{}", .{value});
self.popState();
return;
}
},
.ComptimeInt => {
return self.emitNumber(@as(std.math.IntFittingRange(value, value), value));
},
.Float, .ComptimeFloat => if (@floatCast(f64, value) == value) {
try self.stream.print("{}", .{@floatCast(f64, value)});
self.popState();
return;
},
else => {},
}
try self.stream.print("\"{}\"", .{value});
self.popState();
}
pub fn emitString(self: *Self, string: []const u8) !void {
assert(self.state[self.state_index] == State.Value);
try self.writeEscapedString(string);
self.popState();
}
fn writeEscapedString(self: *Self, string: []const u8) !void {
assert(std.unicode.utf8ValidateSlice(string));
try self.stringify(string);
}
pub fn emitJson(self: *Self, json: std.json.Value) Stream.Error!void {
assert(self.state[self.state_index] == State.Value);
try self.stringify(json);
self.popState();
}
fn indent(self: *Self) !void {
assert(self.state_index >= 1);
try self.stream.writeByte('\n');
try self.whitespace.outputIndent(self.stream);
}
fn pushState(self: *Self, state: State) void {
self.state_index += 1;
self.state[self.state_index] = state;
}
fn popState(self: *Self) void {
self.state_index -= 1;
}
fn stringify(self: *Self, value: anytype) !void {
try std.json.stringify(value, std.json.StringifyOptions{
.whitespace = self.whitespace,
}, self.stream);
}
};
}
pub fn writeStream(
out_stream: anytype,
comptime max_depth: usize,
) WriteStream(@TypeOf(out_stream), max_depth) {
return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
}
test "json write stream" {
var out_buf: [1024]u8 = undefined;
var slice_stream = std.io.fixedBufferStream(&out_buf);
const out = slice_stream.outStream();
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_allocator.deinit();
var w = std.json.writeStream(out, 10);
try w.beginObject();
try w.objectField("object");
try w.emitJson(try getJsonObject(&arena_allocator.allocator));
try w.objectField("string");
try w.emitString("This is a string");
try w.objectField("array");
try w.beginArray();
try w.arrayElem();
try w.emitString("Another string");
try w.arrayElem();
try w.emitNumber(@as(i32, 1));
try w.arrayElem();
try w.emitNumber(@as(f32, 3.5));
try w.endArray();
try w.objectField("int");
try w.emitNumber(@as(i32, 10));
try w.objectField("float");
try w.emitNumber(@as(f32, 3.5));
try w.endObject();
const result = slice_stream.getWritten();
const expected =
\\{
\\ "object": {
\\ "one": 1,
\\ "two": 2.0e+00
\\ },
\\ "string": "This is a string",
\\ "array": [
\\ "Another string",
\\ 1,
\\ 3.5e+00
\\ ],
\\ "int": 10,
\\ "float": 3.5e+00
\\}
;
std.testing.expect(std.mem.eql(u8, expected, result));
}
fn getJsonObject(allocator: *std.mem.Allocator) !std.json.Value {
var value = std.json.Value{ .Object = std.json.ObjectMap.init(allocator) };
_ = try value.Object.put("one", std.json.Value{ .Integer = @intCast(i64, 1) });
_ = try value.Object.put("two", std.json.Value{ .Float = 2.0 });
return value;
}

View file

@ -18,7 +18,7 @@ const Info = @import("types/info.zig");
const debug_allocator = @import("build_options").debug_allocator; const debug_allocator = @import("build_options").debug_allocator;
// Set the log level to warning // Set the log level to warning
//pub const log_level: std.log.Level = .warn; pub const log_level: std.log.Level = .warn;
// Define root.log to override the std implementation // Define root.log to override the std implementation
pub fn log( pub fn log(
comptime level: std.log.Level, comptime level: std.log.Level,
@ -63,7 +63,6 @@ pub fn main() !void {
if (!debug_allocator) arena.deinit(); if (!debug_allocator) arena.deinit();
} }
var bar = barImpl.initBar(allocator); var bar = barImpl.initBar(allocator);
var br = Bar.init(&bar); var br = Bar.init(&bar);
@ -78,8 +77,8 @@ pub fn main() !void {
bar.widgets = widgets[0..]; bar.widgets = widgets[0..];
try br.start(); try br.start();
if (debug_allocator) { if (debug_allocator) {
std.log.debug(.main, "Finished cleanup, last allocation info.\n", .{}); std.debug.print("Finished cleanup, last allocation info.\n", .{});
std.log.debug(.main, "\n{}\n", .{dbgAlloc.info}); std.debug.print("\n{}\n", .{dbgAlloc.info});
dbgAlloc.printRemainingStackTraces(); dbgAlloc.printRemainingStackTraces();
dbgAlloc.deinit(); dbgAlloc.deinit();
} }

View file

@ -129,6 +129,19 @@ pub const MemoryWidget = struct {
self.update_bar() catch {}; self.update_bar() catch {};
} }
pub fn clear_cache(self: *MemoryWidget) !void {
var buffer: [512 * 512]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
var allocator = &fba.allocator;
var proc = try std.ChildProcess.init(&[_][]const u8{ "bash", "/home/kitteh/Scripts/drop-cache.sh" }, allocator);
proc.stdout_behavior = .Close;
proc.stdin_behavior = .Close;
proc.stderr_behavior = .Close;
try proc.spawn();
_ = try proc.kill();
proc.deinit();
}
fn update_bar(self: *MemoryWidget) !void { fn update_bar(self: *MemoryWidget) !void {
var buffer: [512]u8 = undefined; var buffer: [512]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer); var fba = std.heap.FixedBufferAllocator.init(&buffer);
@ -191,6 +204,11 @@ pub const MemoryWidget = struct {
.full_text = text, .full_text = text,
.markup = "pango", .markup = "pango",
}); });
if (kibibytesToMegabytes(memInfo.cached) > 1000) {
self.clear_cache() catch |err| {
std.log.err(.memory, "Can't clear cache {}.\n", .{err});
};
}
} }
pub fn start(self: *MemoryWidget) anyerror!void { pub fn start(self: *MemoryWidget) anyerror!void {

View file

@ -64,7 +64,7 @@ pub const NetworkWidget = struct {
network_infos: std.ArrayList(NetworkInfo), network_infos: std.ArrayList(NetworkInfo),
num_interfaces: u8 = 0, num_interfaces: u8 = 0,
current_interface: u8 = 0, current_interface: u8 = 0,
update_mutex: std.Mutex = std.Mutex.init(), update_mutex: std.Mutex = std.Mutex{},
pub fn name(self: *NetworkWidget) []const u8 { pub fn name(self: *NetworkWidget) []const u8 {
return "network"; return "network";
@ -94,12 +94,16 @@ pub const NetworkWidget = struct {
pub fn update_network_infos(self: *NetworkWidget) anyerror!void { pub fn update_network_infos(self: *NetworkWidget) anyerror!void {
const lock = self.update_mutex.acquire(); const lock = self.update_mutex.acquire();
defer lock.release(); defer lock.release();
std.log.debug(.network, "Updating network info.\n", .{});
for (self.network_infos.items) |info| { for (self.network_infos.items) |info| {
freeString(self.allocator, info.network_info); freeString(self.allocator, info.network_info);
} }
self.num_interfaces = 0; self.num_interfaces = 0;
var proc = try std.ChildProcess.init(&[_][]const u8{ "nmcli", "-f", "common", "-c", "no", "d" }, self.allocator); var proc = try std.ChildProcess.init(&[_][]const u8{ "nmcli", "-f", "common", "-c", "no", "d" }, self.allocator);
defer { _ = proc.kill() catch {}; proc.deinit(); } defer {
_ = proc.kill() catch {};
proc.deinit();
}
proc.stdout_behavior = .Pipe; proc.stdout_behavior = .Pipe;
try proc.spawn(); try proc.spawn();
var i: u8 = 0; var i: u8 = 0;