Compare commits

...

2 Commits

Author SHA1 Message Date
Jarred Sumner
672b30b513 Merge branch 'main' into claude/refactor-fetchheaders-copyto-memcpyspan 2025-11-30 17:00:48 -08:00
Claude Bot
063e33fb9f refactor(fetch): use WTF::memcpySpan and std::span in FetchHeaders::copyTo
- Replace raw memcpy with WTF::memcpySpan for safer buffer operations
- Use std::span throughout the internal implementation
- Add thin extern "C" wrapper that converts ptr+len to spans
- Extract copyString lambda to reduce code duplication
- Track offset with simple counter instead of pointer arithmetic
- Empty values use {0, 0} instead of current buffer position
- Zig bindings now pass slices instead of raw pointers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 07:57:12 +00:00
4 changed files with 45 additions and 41 deletions

View File

@@ -3,7 +3,7 @@ pub const FetchHeaders = opaque {
extern fn WebCore__FetchHeaders__cast_(JSValue0: JSValue, arg1: *VM) ?*FetchHeaders;
extern fn WebCore__FetchHeaders__clone(arg0: *FetchHeaders, arg1: *JSGlobalObject) JSValue;
extern fn WebCore__FetchHeaders__cloneThis(arg0: *FetchHeaders, arg1: *JSGlobalObject) *FetchHeaders;
extern fn WebCore__FetchHeaders__copyTo(arg0: *FetchHeaders, arg1: [*]StringPointer, arg2: [*]StringPointer, arg3: [*]u8) void;
extern fn WebCore__FetchHeaders__copyTo(arg0: *FetchHeaders, arg1: [*]StringPointer, arg2: [*]StringPointer, arg3: [*]u8, arg4: usize, arg5: usize) void;
extern fn WebCore__FetchHeaders__count(arg0: *FetchHeaders, arg1: *u32, arg2: *u32) void;
extern fn WebCore__FetchHeaders__createEmpty() *FetchHeaders;
extern fn WebCore__FetchHeaders__createFromPicoHeaders_(arg0: ?*const anyopaque) *FetchHeaders;
@@ -422,15 +422,17 @@ pub const FetchHeaders = opaque {
pub fn copyTo(
this: *FetchHeaders,
names: [*]api.StringPointer,
values: [*]api.StringPointer,
buf: [*]u8,
names: []api.StringPointer,
values: []api.StringPointer,
buf: []u8,
) void {
return WebCore__FetchHeaders__copyTo(
this,
names,
values,
buf,
names.ptr,
values.ptr,
buf.ptr,
buf.len,
names.len,
);
}
};

View File

@@ -1856,52 +1856,54 @@ bool WebCore__FetchHeaders__fastHas_(WebCore::FetchHeaders* arg0, unsigned char
return arg0->fastHas(static_cast<HTTPHeaderName>(HTTPHeaderName1));
}
void WebCore__FetchHeaders__copyTo(WebCore::FetchHeaders* headers, StringPointer* names, StringPointer* values, unsigned char* buf)
static void copyHeadersTo(WebCore::FetchHeaders* headers, std::span<StringPointer> names, std::span<StringPointer> values, std::span<unsigned char> buf)
{
auto iter = headers->createIterator();
unsigned int i = 0;
size_t i = 0;
uint32_t off = 0;
auto copyString = [&buf, &off](const WTF::String& str) -> StringPointer {
const uint32_t start = off;
if (str.is8Bit() && str.containsOnlyASCII()) {
const auto srcSpan = str.span8();
WTF::memcpySpan(buf.first(srcSpan.size()), srcSpan);
buf = buf.subspan(srcSpan.size());
off += str.length();
return { start, str.length() };
} else {
// Convert to UTF-8 for non-ASCII or UTF-16 strings
WTF::CString cString = str.utf8();
const auto srcSpan = cString.span();
WTF::memcpySpan(buf.first(srcSpan.size()), srcSpan);
buf = buf.subspan(srcSpan.size());
off += static_cast<uint32_t>(srcSpan.size());
return { start, static_cast<uint32_t>(srcSpan.size()) };
}
};
for (auto pair = iter.next(); pair; pair = iter.next()) {
const auto name = pair->key;
const auto value = pair->value;
const auto& name = pair->key;
const auto& value = pair->value;
ASSERT_WITH_MESSAGE(name.length(), "Header name must not be empty");
ASSERT_WITH_MESSAGE(name.containsOnlyASCII(), "Header name must be ASCII. This should already be validated before calling this function.");
if (name.is8Bit() && name.containsOnlyASCII()) {
const auto nameSpan = name.span8();
memcpy(&buf[i], nameSpan.data(), nameSpan.size());
*names = { i, name.length() };
i += name.length();
} else {
ASSERT_WITH_MESSAGE(name.containsOnlyASCII(), "Header name must be ASCII. This should already be validated before calling this function.");
WTF::CString nameCString = name.utf8();
memcpy(&buf[i], nameCString.data(), nameCString.length());
*names = { i, static_cast<uint32_t>(nameCString.length()) };
i += static_cast<uint32_t>(nameCString.length());
}
names[i] = copyString(name);
if (value.length() > 0) {
if (value.is8Bit() && value.containsOnlyASCII()) {
const auto valueSpan = value.span8();
memcpy(&buf[i], valueSpan.data(), valueSpan.size());
*values = { i, value.length() };
i += value.length();
} else {
// HTTP headers can contain non-ASCII characters according to RFC 7230
// Non-ASCII content should be properly encoded
WTF::CString valueCString = value.utf8();
memcpy(&buf[i], valueCString.data(), valueCString.length());
*values = { i, static_cast<uint32_t>(valueCString.length()) };
i += static_cast<uint32_t>(valueCString.length());
}
values[i] = copyString(value);
} else {
*values = { i, 0 };
values[i] = { 0, 0 };
}
names++;
values++;
i++;
}
}
void WebCore__FetchHeaders__copyTo(WebCore::FetchHeaders* headers, StringPointer* names, StringPointer* values, unsigned char* buf, size_t buf_len, size_t header_count)
{
copyHeadersTo(headers, { names, header_count }, { values, header_count }, { buf, buf_len });
}
void WebCore__FetchHeaders__count(WebCore::FetchHeaders* headers, uint32_t* count, uint32_t* buf_len)
{
auto iter = headers->createIterator();

View File

@@ -81,7 +81,7 @@ CPP_DECL void WebCore__FetchHeaders__append(WebCore::FetchHeaders* arg0, const Z
CPP_DECL WebCore::FetchHeaders* WebCore__FetchHeaders__cast_(JSC::EncodedJSValue JSValue0, JSC::VM* arg1);
CPP_DECL JSC::EncodedJSValue WebCore__FetchHeaders__clone(WebCore::FetchHeaders* arg0, JSC::JSGlobalObject* arg1);
CPP_DECL WebCore::FetchHeaders* WebCore__FetchHeaders__cloneThis(WebCore::FetchHeaders* arg0, JSC::JSGlobalObject* arg1);
CPP_DECL void WebCore__FetchHeaders__copyTo(WebCore::FetchHeaders* arg0, StringPointer* arg1, StringPointer* arg2, unsigned char* arg3);
CPP_DECL void WebCore__FetchHeaders__copyTo(WebCore::FetchHeaders* arg0, StringPointer* arg1, StringPointer* arg2, unsigned char* arg3, size_t arg4, size_t arg5);
CPP_DECL void WebCore__FetchHeaders__count(WebCore::FetchHeaders* arg0, uint32_t* arg1, uint32_t* arg2);
CPP_DECL WebCore::FetchHeaders* WebCore__FetchHeaders__createEmpty();
CPP_DECL WebCore::FetchHeaders* WebCore__FetchHeaders__createFromJS(JSC::JSGlobalObject* arg0, JSC::EncodedJSValue JSValue1);

View File

@@ -169,7 +169,7 @@ pub fn from(fetch_headers_ref: ?*FetchHeaders, allocator: std.mem.Allocator, opt
var names = sliced.items(.name);
var values = sliced.items(.value);
if (fetch_headers_ref) |headers_ref|
headers_ref.copyTo(names.ptr, values.ptr, headers.buf.items.ptr);
headers_ref.copyTo(names, values, headers.buf.items);
// TODO: maybe we should send Content-Type header first instead of last?
if (needs_content_type) {