An HTTP/1.1 server for zig
0

Configure Feed

Select the types of activity you want to include in your feed.

key_value.zig and params.zig: fix memory leaks and (#138)

* key_value.zig and params.zig: fix memory leaks and
use single allocation

params.zig and key_value.zig: could leak memory if
2nd (or 3rd) allocation failed. This is now fixed.
Number of Allocations is also reduced to 1.

* made the changes to key_value and params non-breaking

changes to key_value and params no longer break
anything on the user side.

+36 -45
+24 -28
src/key_value.zig
··· 4 4 const ascii = std.ascii; 5 5 const Allocator = std.mem.Allocator; 6 6 7 - fn KeyValue(K: type, V: type, equalFn: fn (lhs: K, rhs: K) callconv(.Inline) bool, hashFn: fn (key: K) callconv(.Inline) u8) type { 7 + fn KeyValue(V: type, hashFn: fn (key: []const u8) callconv(.Inline) u8) type { 8 8 return struct { 9 9 len: usize, 10 - keys: []K, 10 + keys: [][]const u8, 11 11 values: []V, 12 12 hashes: []u8, 13 13 14 + const Self = @This(); 14 15 pub const Value = V; 15 16 16 - const Self = @This(); 17 + const alignment = @max(@alignOf([]const u8), @alignOf(V)); 18 + const size = @sizeOf([]const u8) + @sizeOf(V) + @sizeOf(u8); 19 + const kFirst = @alignOf([]const u8) >= @alignOf(V); 17 20 18 - pub fn init(allocator: Allocator, max: usize) !Self { 21 + pub fn init(allocator: Allocator, max: usize) Allocator.Error!Self { 22 + // we want type with bigger alignment to be first. 23 + // Since alignment is always a power of 2, the second type is guaranteed to have correct alignment. 24 + const allocation = try allocator.alignedAlloc(u8, alignment, max * size); 19 25 return .{ 20 26 .len = 0, 21 - .keys = try allocator.alloc(K, max), 22 - .values = try allocator.alloc(V, max), 23 - .hashes = try allocator.alloc(u8, max), 27 + .keys = @as([*][]const u8, @alignCast(@ptrCast(if (kFirst) allocation.ptr else allocation[max * @sizeOf(V)..].ptr)))[0..max], 28 + .values = @as([*]V, @alignCast(@ptrCast(if (kFirst) allocation[max * @sizeOf([]const u8)..].ptr else allocation.ptr)))[0..max], 29 + .hashes = allocation[max * @sizeOf([]const u8) + max * @sizeOf(V)..], 24 30 }; 25 31 } 26 32 27 33 pub fn deinit(self: *Self, allocator: Allocator) void { 28 - allocator.free(self.keys); 29 - allocator.free(self.values); 30 - allocator.free(self.hashes); 34 + const allocation = @as([*] align(alignment) u8, @alignCast(@ptrCast(if (kFirst) self.keys.ptr else self.values.ptr))); 35 + allocator.free(allocation[0..self.keys.len * size]); 31 36 } 32 37 33 - pub fn add(self: *Self, key: K, value: V) void { 38 + pub fn add(self: *Self, key: []const u8, value: V) void { 34 39 const len = self.len; 40 + const max = self.keys.len; 35 41 var keys = self.keys; 36 - if (len == keys.len) { 42 + if (len == max) { 37 43 return; 38 44 } 39 45 ··· 43 49 self.len = len + 1; 44 50 } 45 51 46 - pub fn get(self: *const Self, key: K) ?V { 52 + pub fn get(self: *const Self, key: []const u8) ?V { 47 53 const hash = hashFn(key); 48 54 for (self.hashes[0..self.len], 0..) |h, i| { 49 - if (h == hash and equalFn(self.keys[i], key)) { 55 + if (h == hash and std.mem.eql(u8, self.keys[i], key)) { 50 56 return self.values[i]; 51 57 } 52 58 } 53 59 return null; 54 60 } 55 61 56 - pub fn has(self: *const Self, key: K) bool { 57 - const hash = hashFn(key); 58 - for (self.hashes[0..self.len], 0..) |h, i| { 59 - if (h == hash and equalFn(self.keys[i], key)) { 60 - return true; 61 - } 62 - } 63 - return false; 62 + pub fn has(self: *const Self, key: []const u8) bool { 63 + return self.get(key) != null; 64 64 } 65 65 66 66 pub fn reset(self: *Self) void { ··· 109 109 return @as(u8, @truncate(key.len)) | (key[0]) ^ (key[key.len - 1]); 110 110 } 111 111 112 - inline fn strEql(lhs: []const u8, rhs: []const u8) bool { 113 - return std.mem.eql(u8, lhs, rhs); 114 - } 115 - 116 - pub const StringKeyValue = KeyValue([]const u8, []const u8, strEql, strHash); 112 + pub const StringKeyValue = KeyValue([]const u8, strHash); 117 113 118 114 const MultiForm = struct { 119 115 value: []const u8, 120 116 filename: ?[]const u8 = null, 121 117 }; 122 - pub const MultiFormKeyValue = KeyValue([]const u8, MultiForm, strEql, strHash); 118 + pub const MultiFormKeyValue = KeyValue(MultiForm, strHash); 123 119 124 120 const t = @import("t.zig"); 125 121 test "KeyValue: get" {
+12 -17
src/params.zig
··· 16 16 values: [][]const u8, 17 17 18 18 pub fn init(allocator: Allocator, max: usize) !Params { 19 - const names = try allocator.alloc([]const u8, max); 20 - const values = try allocator.alloc([]const u8, max); 19 + const allocation = try allocator.alloc([]const u8, 2 * max); 21 20 return .{ 22 21 .len = 0, 23 - .names = names, 24 - .values = values, 22 + .names = allocation[0..max], 23 + .values = allocation[max..], 25 24 }; 26 25 } 27 26 28 27 pub fn deinit(self: *Params, allocator: Allocator) void { 29 - allocator.free(self.names); 30 - allocator.free(self.values); 28 + allocator.free(self.names.ptr[0 .. 2 * self.names.len]); 31 29 } 32 30 33 31 pub fn addValue(self: *Params, value: []const u8) void { 34 - const len = self.len; 35 - const values = self.values; 36 - if (len == values.len) { 32 + if (self.len == self.names.len) { 37 33 return; 38 34 } 39 - values[len] = value; 40 - self.len = len + 1; 35 + self.values[self.len] = value; 36 + self.len += 1; 41 37 } 42 38 43 39 // It should be impossible for names.len != self.len at this point, but it's 44 40 // a bit dangerous to assume that since self.names is re-used between requests 45 41 // and we don't want to leak anything, so I think enforcing a len of names.len 46 42 // is safer, since names is generally statically defined based on routes setup. 47 - pub fn addNames(self: *Params, names: [][]const u8) void { 43 + // 44 + // `noalias names` as `names` being a pointer inside self.names doesn't make sense, 45 + // and memcpy needs this guarantee anyways. 46 + pub fn addNames(self: *Params, noalias names: [][]const u8) void { 48 47 std.debug.assert(names.len == self.len); 49 - const n = self.names; 50 - for (names, 0..) |name, i| { 51 - n[i] = name; 52 - } 53 - self.len = names.len; 48 + @memcpy(self.names[0..self.len], names); 54 49 } 55 50 56 51 pub fn get(self: *const Params, needle: []const u8) ?[]const u8 {