Commit c1e7d0c08f

Jacob Young <jacobly0@users.noreply.github.com>
2024-01-31 14:33:17
http: optimize allocations for proxy basic authorization
1 parent a111f80
Changed files (2)
lib/std/http/Client.zig
@@ -1141,8 +1141,10 @@ pub fn loadDefaultProxies(client: *Client) !void {
         };
 
         if (uri.user != null or uri.password != null) {
-            var authorization: [basic_authorization.max_value_len]u8 = undefined;
-            try client.http_proxy.?.headers.append("proxy-authorization", basic_authorization.value(uri, &authorization));
+            const authorization = try client.allocator.alloc(u8, basic_authorization.valueLengthFromUri(uri));
+            errdefer client.allocator.free(authorization);
+            std.debug.assert(basic_authorization.value(uri, authorization).len == authorization.len);
+            try client.http_proxy.?.headers.appendOwned(.{ .unowned = "proxy-authorization" }, .{ .owned = authorization });
         }
     }
 
@@ -1182,8 +1184,10 @@ pub fn loadDefaultProxies(client: *Client) !void {
         };
 
         if (uri.user != null or uri.password != null) {
-            var authorization: [basic_authorization.max_value_len]u8 = undefined;
-            try client.https_proxy.?.headers.append("proxy-authorization", basic_authorization.value(uri, &authorization));
+            const authorization = try client.allocator.alloc(u8, basic_authorization.valueLengthFromUri(uri));
+            errdefer client.allocator.free(authorization);
+            std.debug.assert(basic_authorization.value(uri, authorization).len == authorization.len);
+            try client.https_proxy.?.headers.appendOwned(.{ .unowned = "proxy-authorization" }, .{ .owned = authorization });
         }
     }
 }
lib/std/http/Headers.zig
@@ -91,30 +91,64 @@ pub const Headers = struct {
     ///
     /// If the `owned` field is true, both name and value will be copied.
     pub fn append(headers: *Headers, name: []const u8, value: []const u8) !void {
-        const n = headers.list.items.len;
+        try headers.appendOwned(.{ .unowned = name }, .{ .unowned = value });
+    }
 
-        const value_duped = if (headers.owned) try headers.allocator.dupe(u8, value) else value;
-        errdefer if (headers.owned) headers.allocator.free(value_duped);
+    pub const OwnedString = union(enum) {
+        /// A string allocated by the `allocator` field.
+        owned: []u8,
+        /// A string to be copied by the `allocator` field.
+        unowned: []const u8,
+    };
 
-        var entry = Field{ .name = undefined, .value = value_duped };
+    /// Appends a header to the list.
+    ///
+    /// If the `owned` field is true, `name` and `value` will be copied if unowned.
+    pub fn appendOwned(headers: *Headers, name: OwnedString, value: OwnedString) !void {
+        const n = headers.list.items.len;
+        try headers.list.ensureUnusedCapacity(headers.allocator, 1);
+
+        const owned_value = switch (value) {
+            .owned => |owned| owned,
+            .unowned => |unowned| if (headers.owned)
+                try headers.allocator.dupe(u8, unowned)
+            else
+                unowned,
+        };
+        errdefer if (value == .unowned and headers.owned) headers.allocator.free(owned_value);
+
+        var entry = Field{ .name = undefined, .value = owned_value };
+
+        if (headers.index.getEntry(switch (name) {
+            inline else => |string| string,
+        })) |kv| {
+            defer switch (name) {
+                .owned => |owned| headers.allocator.free(owned),
+                .unowned => {},
+            };
 
-        if (headers.index.getEntry(name)) |kv| {
             entry.name = kv.key_ptr.*;
             try kv.value_ptr.append(headers.allocator, n);
         } else {
-            const name_duped = if (headers.owned) try std.ascii.allocLowerString(headers.allocator, name) else name;
-            errdefer if (headers.owned) headers.allocator.free(name_duped);
+            const owned_name = switch (name) {
+                .owned => |owned| owned,
+                .unowned => |unowned| if (headers.owned)
+                    try std.ascii.allocLowerString(headers.allocator, unowned)
+                else
+                    unowned,
+            };
+            errdefer if (name == .unowned and headers.owned) headers.allocator.free(owned_name);
 
-            entry.name = name_duped;
+            entry.name = owned_name;
 
             var new_index = try HeaderIndexList.initCapacity(headers.allocator, 1);
             errdefer new_index.deinit(headers.allocator);
 
             new_index.appendAssumeCapacity(n);
-            try headers.index.put(headers.allocator, name_duped, new_index);
+            try headers.index.put(headers.allocator, owned_name, new_index);
         }
 
-        try headers.list.append(headers.allocator, entry);
+        headers.list.appendAssumeCapacity(entry);
     }
 
     /// Returns true if this list of headers contains the given name.