diff --git a/src/format.hpp b/src/format.hpp index ace8d93..5a50c63 100644 --- a/src/format.hpp +++ b/src/format.hpp @@ -79,6 +79,8 @@ * - **Simple concatenation**: Basic string + number + string combinations * - **Compile-time optimization**: When all types/values known at compile time * - **Template contexts**: Where compile-time buffer sizing is beneficial + * - **IMPORTANT**: Only works with compile-time string literals, NOT runtime + * const char* * * ## Optimization Details: * The function uses `ArenaAllocator::allocate_remaining_space()` to claim all @@ -206,10 +208,12 @@ inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); } * optimized term writers for maximum speed. * * ## Supported Types: - * - **String literals**: C-style string literals and arrays + * - **String literals**: C-style string literals and arrays ("Hello", "World") * - **Integers**: All integral types (int, int64_t, uint32_t, etc.) * - **Floating point**: double (uses high-precision Grisu2 algorithm) * - **Custom types**: Via specialization of `detail::term()` + * - **NOT supported**: const char* variables, std::string, std::string_view + * variables * * ## Performance Characteristics: * - **Compile-time buffer sizing**: Buffer size calculated at compile time (no @@ -245,16 +249,23 @@ inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); } * * ## When to Use: * - **Hot paths**: Performance-critical code where formatting speed matters - * - **Known types**: When argument types are known at compile time + * - **Compile-time string literals**: All string arguments must be string + * literals (e.g., "Hello") * - **Simple formatting**: Concatenation and basic type conversion * - **Template code**: Where compile-time optimization is beneficial + * - **CANNOT use runtime strings**: No const char*, std::string, or string_view + * variables * * ## When to Use format() Instead: * - **Printf-style formatting**: When you need format specifiers like "%d", * "%.2f" - * - **Runtime flexibility**: When format strings come from variables/config - * - **Complex formatting**: When you need padding, precision, etc. - * - **Convenience**: For quick debugging or non-critical paths + * - **Runtime strings**: When you have const char*, std::string, or string_view + * variables + * - **Dynamic content**: When format strings come from variables/config/user + * input + * - **Complex formatting**: When you need padding, precision, width specifiers + * - **Mixed literal/runtime**: When combining string literals with runtime + * string data * * @note All arguments are passed by forwarding reference for optimal * performance diff --git a/src/http_handler.cpp b/src/http_handler.cpp index 0c1d85e..2425ae2 100644 --- a/src/http_handler.cpp +++ b/src/http_handler.cpp @@ -195,8 +195,9 @@ void HttpHandler::handlePostCommit(Connection &conn, const char *error = state.commit_parser ? state.commit_parser->get_parse_error() : "No parser initialized"; - std::string error_msg = "Parse failed: "; - error_msg += error ? error : "Unknown error"; + ArenaAllocator &arena = conn.get_arena(); + std::string_view error_msg = + format(arena, "Parse failed: %s", error ? error : "Unknown error"); send_error_response(conn, 400, error_msg, state.connection_close); return; } @@ -319,57 +320,47 @@ void HttpHandler::handleNotFound(Connection &conn, void HttpHandler::sendResponse(Connection &conn, int status_code, std::string_view content_type, std::string_view body, bool close_connection) { - [[maybe_unused]] ArenaAllocator &arena = conn.get_arena(); - - // Build HTTP response using arena - std::string response; - response.reserve(256 + body.size()); - - response += "HTTP/1.1 "; - response += std::to_string(status_code); - response += " "; - - // Status text - switch (status_code) { - case 200: - response += "OK"; - break; - case 400: - response += "Bad Request"; - break; - case 404: - response += "Not Found"; - break; - case 500: - response += "Internal Server Error"; - break; - default: - response += "Unknown"; - break; - } - + ArenaAllocator &arena = conn.get_arena(); auto *state = static_cast(conn.user_data); - response += "\r\n"; - response += "Content-Type: "; - response += content_type; - response += "\r\n"; - response += "Content-Length: "; - response += std::to_string(body.size()); - response += "\r\n"; - response += "X-Response-ID: "; - response += std::to_string(state->request_id); - response += "\r\n"; - - if (close_connection) { - response += "Connection: close\r\n"; - conn.close_after_send(); // Signal connection should be closed after sending - } else { - response += "Connection: keep-alive\r\n"; + // Status text + std::string_view status_text; + switch (status_code) { + case 200: + status_text = "OK"; + break; + case 400: + status_text = "Bad Request"; + break; + case 404: + status_text = "Not Found"; + break; + case 500: + status_text = "Internal Server Error"; + break; + default: + status_text = "Unknown"; + break; } - response += "\r\n"; - response += body; + const char *connection_header = close_connection ? "close" : "keep-alive"; + + std::string_view response = + format(arena, + "HTTP/1.1 %d %.*s\r\n" + "Content-Type: %.*s\r\n" + "Content-Length: %zu\r\n" + "X-Response-ID: %ld\r\n" + "Connection: %s\r\n" + "\r\n%.*s", + status_code, static_cast(status_text.size()), + status_text.data(), static_cast(content_type.size()), + content_type.data(), body.size(), state->request_id, + connection_header, static_cast(body.size()), body.data()); + + if (close_connection) { + conn.close_after_send(); + } conn.append_message(response); } @@ -383,11 +374,11 @@ void HttpHandler::send_json_response(Connection &conn, int status_code, void HttpHandler::send_error_response(Connection &conn, int status_code, std::string_view message, bool close_connection) { - [[maybe_unused]] ArenaAllocator &arena = conn.get_arena(); + ArenaAllocator &arena = conn.get_arena(); - std::string json = R"({"error":")"; - json += message; - json += R"("})"; + std::string_view json = + format(arena, R"({"error":"%.*s"})", static_cast(message.size()), + message.data()); send_json_response(conn, status_code, json, close_connection); } diff --git a/style.md b/style.md index 689ab94..1ad375f 100644 --- a/style.md +++ b/style.md @@ -102,6 +102,27 @@ auto addr = reinterpret_cast(ptr); // Pointer to integer conv - **String views** with `std::string_view` to minimize unnecessary copying - **Arena allocation** for efficient memory management (~1ns vs ~20-270ns for malloc) +### String Formatting +- **Always use `format.hpp` functions** - formats directly into arena-allocated memory +- **Use `static_format()` for performance-sensitive code** - faster but less flexible than `format()` +- **Use `format()` function with arena allocator** for printf-style formatting +```cpp +// Most performance-sensitive - compile-time optimized concatenation +std::string_view response = static_format(arena, + "HTTP/1.1 ", status_code, " OK\r\n", + "Content-Length: ", body.size(), "\r\n", + "\r\n", body); + +// Printf-style formatting - runtime flexible +ArenaAllocator& arena = conn.get_arena(); +std::string_view response = format(arena, + "HTTP/1.1 %d OK\r\n" + "Content-Length: %zu\r\n" + "\r\n%.*s", + status_code, body.size(), + static_cast(body.size()), body.data()); +``` + ### Complexity Control - **Encapsulation is the main tool for controlling complexity** - **Header files define the interface** - they are the contract with users of your code