mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
saner mem management, thread-safety
This commit is contained in:
parent
50c64e0f90
commit
1f8e8ac0e4
5 changed files with 107 additions and 38 deletions
|
@ -11,6 +11,9 @@ var alloc: std.mem.Allocator = undefined;
|
||||||
var endpoint: zap.SimpleEndpoint = undefined;
|
var endpoint: zap.SimpleEndpoint = undefined;
|
||||||
var users: Users = undefined;
|
var users: Users = undefined;
|
||||||
|
|
||||||
|
// 100MB of json buffer
|
||||||
|
var jsonbuf: [100 * 1024 * 1024]u8 = undefined;
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
a: std.mem.Allocator,
|
a: std.mem.Allocator,
|
||||||
user_path: []const u8,
|
user_path: []const u8,
|
||||||
|
@ -53,7 +56,7 @@ fn getUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
}
|
}
|
||||||
if (userIdFromPath(path)) |id| {
|
if (userIdFromPath(path)) |id| {
|
||||||
if (users.get(id)) |user| {
|
if (users.get(id)) |user| {
|
||||||
if (zap.stringify(user, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, user, .{})) |json| {
|
||||||
_ = r.sendJson(json);
|
_ = r.sendJson(json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +70,7 @@ fn listUsers(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
if (users.list(&l)) {} else |_| {
|
if (users.list(&l)) {} else |_| {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (zap.stringifyArrayList(User, &l, .{})) |maybe_json| {
|
if (zap.stringifyArrayListBuf(&jsonbuf, User, &l, .{})) |maybe_json| {
|
||||||
if (maybe_json) |json| {
|
if (maybe_json) |json| {
|
||||||
_ = r.sendJson(json);
|
_ = r.sendJson(json);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +91,7 @@ fn postUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
if (maybe_user) |u| {
|
if (maybe_user) |u| {
|
||||||
defer std.json.parseFree(User, u, .{ .allocator = alloc });
|
defer std.json.parseFree(User, u, .{ .allocator = alloc });
|
||||||
if (users.addByName(u.first_name, u.last_name)) |id| {
|
if (users.addByName(u.first_name, u.last_name)) |id| {
|
||||||
if (zap.stringify(.{ .status = "OK", .id = id }, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
||||||
_ = r.sendJson(json);
|
_ = r.sendJson(json);
|
||||||
}
|
}
|
||||||
} else |_| {
|
} else |_| {
|
||||||
|
@ -117,14 +120,14 @@ fn putUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
.{ .allocator = alloc },
|
.{ .allocator = alloc },
|
||||||
);
|
);
|
||||||
if (users.update(id, u.first_name, u.last_name)) {
|
if (users.update(id, u.first_name, u.last_name)) {
|
||||||
if (zap.stringify(.{
|
if (zap.stringifyBuf(&jsonbuf, .{
|
||||||
.status = "OK",
|
.status = "OK",
|
||||||
.id = id,
|
.id = id,
|
||||||
}, .{})) |json| {
|
}, .{})) |json| {
|
||||||
_ = r.sendJson(json);
|
_ = r.sendJson(json);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (zap.stringify(.{
|
if (zap.stringifyBuf(&jsonbuf, .{
|
||||||
.status = "ERROR",
|
.status = "ERROR",
|
||||||
.id = id,
|
.id = id,
|
||||||
}, .{})) |json| {
|
}, .{})) |json| {
|
||||||
|
@ -143,11 +146,11 @@ fn deleteUser(e: *zap.SimpleEndpoint, r: zap.SimpleRequest) void {
|
||||||
if (r.path) |path| {
|
if (r.path) |path| {
|
||||||
if (userIdFromPath(path)) |id| {
|
if (userIdFromPath(path)) |id| {
|
||||||
if (users.delete(id)) {
|
if (users.delete(id)) {
|
||||||
if (zap.stringify(.{ .status = "OK", .id = id }, .{})) |json| {
|
if (zap.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| {
|
||||||
_ = r.sendJson(json);
|
_ = r.sendJson(json);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (zap.stringify(.{
|
if (zap.stringifyBuf(&jsonbuf, .{
|
||||||
.status = "ERROR",
|
.status = "ERROR",
|
||||||
.id = id,
|
.id = id,
|
||||||
}, .{})) |json| {
|
}, .{})) |json| {
|
||||||
|
|
|
@ -3,7 +3,10 @@ const zap = @import("zap");
|
||||||
const Endpoint = @import("endpoint.zig");
|
const Endpoint = @import("endpoint.zig");
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const allocator = std.heap.page_allocator;
|
var gpa = std.heap.GeneralPurposeAllocator(.{
|
||||||
|
.thread_safe = true,
|
||||||
|
}){};
|
||||||
|
var allocator = gpa.allocator();
|
||||||
// setup listener
|
// setup listener
|
||||||
var listener = zap.SimpleEndpointListener.init(
|
var listener = zap.SimpleEndpointListener.init(
|
||||||
allocator,
|
allocator,
|
||||||
|
|
|
@ -32,10 +32,7 @@ pub fn init(a: std.mem.Allocator) Self {
|
||||||
// the request will be freed (and its mem reused by facilio) when it's
|
// the request will be freed (and its mem reused by facilio) when it's
|
||||||
// completed, so we take copies of the names
|
// completed, so we take copies of the names
|
||||||
pub fn addByName(self: *Self, first: ?[]const u8, last: ?[]const u8) !usize {
|
pub fn addByName(self: *Self, first: ?[]const u8, last: ?[]const u8) !usize {
|
||||||
// TODO: get rid of the temp allocation here
|
var user: InternalUser = undefined;
|
||||||
var temp = try self.alloc.alloc(InternalUser, 1);
|
|
||||||
defer self.alloc.free(temp);
|
|
||||||
var user = temp[0];
|
|
||||||
user.firstnamelen = 0;
|
user.firstnamelen = 0;
|
||||||
user.lastnamelen = 0;
|
user.lastnamelen = 0;
|
||||||
if (first) |firstname| {
|
if (first) |firstname| {
|
||||||
|
@ -66,7 +63,7 @@ pub fn delete(self: *Self, id: usize) bool {
|
||||||
pub fn get(self: *Self, id: usize) ?User {
|
pub fn get(self: *Self, id: usize) ?User {
|
||||||
// we don't care about locking here, as our usage-pattern is unlikely to
|
// we don't care about locking here, as our usage-pattern is unlikely to
|
||||||
// get a user by id that is not known yet
|
// get a user by id that is not known yet
|
||||||
if (self.users.get(id)) |pUser| {
|
if (self.users.getPtr(id)) |pUser| {
|
||||||
return .{
|
return .{
|
||||||
.id = pUser.id,
|
.id = pUser.id,
|
||||||
.first_name = pUser.firstnamebuf[0..pUser.firstnamelen],
|
.first_name = pUser.firstnamebuf[0..pUser.firstnamelen],
|
||||||
|
@ -83,11 +80,9 @@ pub fn update(
|
||||||
last: ?[]const u8,
|
last: ?[]const u8,
|
||||||
) bool {
|
) bool {
|
||||||
// we don't care about locking here
|
// we don't care about locking here
|
||||||
var user: ?InternalUser = self.users.get(id);
|
if (self.users.getPtr(id)) |pUser| {
|
||||||
// we got a copy apparently, so we need to put again
|
pUser.firstnamelen = 0;
|
||||||
if (user) |*pUser| {
|
pUser.lastnamelen = 0;
|
||||||
pUser.*.firstnamelen = 0;
|
|
||||||
pUser.*.lastnamelen = 0;
|
|
||||||
if (first) |firstname| {
|
if (first) |firstname| {
|
||||||
std.mem.copy(u8, pUser.firstnamebuf[0..], firstname);
|
std.mem.copy(u8, pUser.firstnamebuf[0..], firstname);
|
||||||
pUser.firstnamelen = firstname.len;
|
pUser.firstnamelen = firstname.len;
|
||||||
|
@ -96,12 +91,6 @@ pub fn update(
|
||||||
std.mem.copy(u8, pUser.lastnamebuf[0..], lastname);
|
std.mem.copy(u8, pUser.lastnamebuf[0..], lastname);
|
||||||
pUser.lastnamelen = lastname.len;
|
pUser.lastnamelen = lastname.len;
|
||||||
}
|
}
|
||||||
_ = self.users.remove(id);
|
|
||||||
if (self.users.put(id, pUser.*)) {
|
|
||||||
return true;
|
|
||||||
} else |_| {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,9 @@ fn on_request(r: zap.SimpleRequest) void {
|
||||||
const user_id: usize = @intCast(usize, the_path[6] - 0x30);
|
const user_id: usize = @intCast(usize, the_path[6] - 0x30);
|
||||||
const user = users.get(user_id);
|
const user = users.get(user_id);
|
||||||
|
|
||||||
|
var buf: [100]u8 = undefined;
|
||||||
var json_to_send: []const u8 = undefined;
|
var json_to_send: []const u8 = undefined;
|
||||||
if (stringify(user, .{})) |json| {
|
if (zap.stringifyBuf(&buf, user, .{})) |json| {
|
||||||
json_to_send = json;
|
json_to_send = json;
|
||||||
} else {
|
} else {
|
||||||
json_to_send = "null";
|
json_to_send = "null";
|
||||||
|
@ -39,17 +40,6 @@ fn setupUserData(a: std.mem.Allocator) !void {
|
||||||
try users.put(2, .{ .first_name = "Your", .last_name = "Mom" });
|
try users.put(2, .{ .first_name = "Your", .last_name = "Mom" });
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf: [100]u8 = undefined;
|
|
||||||
fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8 {
|
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
|
||||||
var string = std.ArrayList(u8).init(fba.allocator());
|
|
||||||
if (std.json.stringify(value, options, string.writer())) {
|
|
||||||
return string.items;
|
|
||||||
} else |_| { // error
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var a = std.heap.page_allocator;
|
var a = std.heap.page_allocator;
|
||||||
try setupUserData(a);
|
try setupUserData(a);
|
||||||
|
|
88
src/util.zig
88
src/util.zig
|
@ -6,8 +6,15 @@ const std = @import("std");
|
||||||
|
|
||||||
// 1MB JSON buffer
|
// 1MB JSON buffer
|
||||||
var jsonbuf: [1024 * 1024]u8 = undefined;
|
var jsonbuf: [1024 * 1024]u8 = undefined;
|
||||||
|
var mutex: std.Thread.Mutex = .{};
|
||||||
|
|
||||||
pub fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8 {
|
/// use default 1MB buffer, mutex-protected
|
||||||
|
pub fn stringify(
|
||||||
|
value: anytype,
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
) ?[]const u8 {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
|
var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
|
||||||
var string = std.ArrayList(u8).init(fba.allocator());
|
var string = std.ArrayList(u8).init(fba.allocator());
|
||||||
if (std.json.stringify(value, options, string.writer())) {
|
if (std.json.stringify(value, options, string.writer())) {
|
||||||
|
@ -17,7 +24,14 @@ pub fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stringifyArrayList(comptime T: anytype, list: *std.ArrayList(T), options: std.json.StringifyOptions) !?[]const u8 {
|
/// use default 1MB buffer, mutex-protected
|
||||||
|
pub fn stringifyArrayList(
|
||||||
|
comptime T: anytype,
|
||||||
|
list: *std.ArrayList(T),
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
) !?[]const u8 {
|
||||||
|
mutex.lock();
|
||||||
|
defer mutex.unlock();
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
|
var fba = std.heap.FixedBufferAllocator.init(&jsonbuf);
|
||||||
var string = std.ArrayList(u8).init(fba.allocator());
|
var string = std.ArrayList(u8).init(fba.allocator());
|
||||||
var writer = string.writer();
|
var writer = string.writer();
|
||||||
|
@ -31,3 +45,73 @@ pub fn stringifyArrayList(comptime T: anytype, list: *std.ArrayList(T), options:
|
||||||
try writer.writeByte(']');
|
try writer.writeByte(']');
|
||||||
return string.items;
|
return string.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// provide your own buf, NOT mutex-protected!
|
||||||
|
pub fn stringifyBuf(
|
||||||
|
buffer: []u8,
|
||||||
|
value: anytype,
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
) ?[]const u8 {
|
||||||
|
var fba = std.heap.FixedBufferAllocator.init(buffer);
|
||||||
|
var string = std.ArrayList(u8).init(fba.allocator());
|
||||||
|
if (std.json.stringify(value, options, string.writer())) {
|
||||||
|
return string.items;
|
||||||
|
} else |_| { // error
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// provide your own buf, NOT mutex-protected
|
||||||
|
pub fn stringifyArrayListBuf(
|
||||||
|
buffer: []u8,
|
||||||
|
comptime T: anytype,
|
||||||
|
list: *std.ArrayList(T),
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
) !?[]const u8 {
|
||||||
|
var fba = std.heap.FixedBufferAllocator.init(buffer);
|
||||||
|
var string = std.ArrayList(u8).init(fba.allocator());
|
||||||
|
var writer = string.writer();
|
||||||
|
try writer.writeByte('[');
|
||||||
|
var first: bool = true;
|
||||||
|
for (list.items) |user| {
|
||||||
|
if (!first) try writer.writeByte(',');
|
||||||
|
first = false;
|
||||||
|
try std.json.stringify(user, options, string.writer());
|
||||||
|
}
|
||||||
|
try writer.writeByte(']');
|
||||||
|
return string.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// provide your own allocator, NOT mutex-protected
|
||||||
|
pub fn stringifyAlloc(
|
||||||
|
a: std.mem.Allocator,
|
||||||
|
value: anytype,
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
) ?[]const u8 {
|
||||||
|
var string = std.ArrayList(u8).init(a);
|
||||||
|
if (std.json.stringify(value, options, string.writer())) {
|
||||||
|
return string.items;
|
||||||
|
} else |_| { // error
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// provide your own allocator, NOT mutex-protected
|
||||||
|
pub fn stringifyArrayListAlloc(
|
||||||
|
a: std.mem.Allocator,
|
||||||
|
comptime T: anytype,
|
||||||
|
list: *std.ArrayList(T),
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
) !?[]const u8 {
|
||||||
|
var string = std.ArrayList(u8).init(a);
|
||||||
|
var writer = string.writer();
|
||||||
|
try writer.writeByte('[');
|
||||||
|
var first: bool = true;
|
||||||
|
for (list.items) |user| {
|
||||||
|
if (!first) try writer.writeByte(',');
|
||||||
|
first = false;
|
||||||
|
try std.json.stringify(user, options, string.writer());
|
||||||
|
}
|
||||||
|
try writer.writeByte(']');
|
||||||
|
return string.items;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue