From 87b2cd844f2b7cbd7c16eee7e4af657864d76505 Mon Sep 17 00:00:00 2001 From: jiuker Date: Tue, 16 Jun 2026 19:12:34 +0800 Subject: [PATCH 1/3] refactor refactor --- include/miniocpp/response.h | 52 ++++++++++++++----------- src/response.cc | 77 +++++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 52 deletions(-) diff --git a/include/miniocpp/response.h b/include/miniocpp/response.h index 031af0e..510f823 100644 --- a/include/miniocpp/response.h +++ b/include/miniocpp/response.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -238,19 +239,21 @@ MINIO_S3_DERIVE_FROM_RESPONSE(DownloadObjectResponse) MINIO_S3_DERIVE_FROM_RESPONSE(GetObjectResponse) struct Item : public Response { - std::string etag; // except DeleteMarker - std::string name; + // etag and name stored in owning ListObjectsResponse's owned_. + std::string_view etag; // except DeleteMarker + std::string_view name; utils::UtcTime last_modified; - std::string owner_id; - std::string owner_name; + // Fields below point into owning ListObjectsResponse's xml_document memory. + std::string_view owner_id; + std::string_view owner_name; size_t size = 0; // except DeleteMarker - std::string storage_class; - bool is_latest = false; // except ListObjects V1/V2 - std::string version_id; // except ListObjects V1/V2 + std::string_view storage_class; + bool is_latest = false; // except ListObjects V1/V2 + std::string_view version_id; // except ListObjects V1/V2 std::map user_metadata; bool is_prefix = false; bool is_delete_marker = false; - std::string encoding_type; + std::string_view encoding_type; Item() = default; @@ -263,29 +266,34 @@ struct Item : public Response { struct ListObjectsResponse : public Response { // Common - std::string name; - std::string encoding_type; - std::string prefix; - std::string delimiter; + std::string_view name; + std::string_view encoding_type; + std::string_view prefix; + std::string_view delimiter; bool is_truncated; unsigned int max_keys; std::list contents; + // Owned XML document for zero-copy string_view backing. + std::shared_ptr doc_; + // Owned strings for non-XML-assigned string_view backing storage. + std::list owned_; + // ListObjectsV1 - std::string marker; - std::string next_marker; + std::string_view marker; + std::string_view next_marker; // ListObjectsV2 unsigned int key_count; - std::string start_after; - std::string continuation_token; - std::string next_continuation_token; + std::string_view start_after; + std::string_view continuation_token; + std::string_view next_continuation_token; // ListObjectVersions - std::string key_marker; - std::string next_key_marker; - std::string version_id_marker; - std::string next_version_id_marker; + std::string_view key_marker; + std::string_view next_key_marker; + std::string_view version_id_marker; + std::string_view next_version_id_marker; ListObjectsResponse() = default; @@ -293,8 +301,6 @@ struct ListObjectsResponse : public Response { explicit ListObjectsResponse(const Response& resp) : Response(resp) {} - ~ListObjectsResponse() = default; - static ListObjectsResponse ParseXML(std::string_view data, bool version); }; // struct ListObjectsResponse diff --git a/src/response.cc b/src/response.cc index b77f545..9ea7aaa 100644 --- a/src/response.cc +++ b/src/response.cc @@ -160,17 +160,31 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, bool version) { ListObjectsResponse resp; - pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + auto xdoc = std::make_shared(); + pugi::xml_parse_result result = xdoc->load_string(data.data()); if (!result) { return error::make("unable to parse XML"); } + resp.doc_ = std::move(xdoc); std::string xpath = version ? "/ListVersionsResult" : "/ListBucketResult"; - auto root = xdoc.select_node(xpath.c_str()); + auto root = resp.doc_->select_node(xpath.c_str()); pugi::xpath_node text; std::string value; + const char* raw = nullptr; + + // Helper to store an unescaped string or point into doc memory. + auto assign_unescaped = [&resp](std::string_view encoding_type, + const char* raw, + std::string_view& target) -> void { + if (encoding_type == "url") { + resp.owned_.emplace_back(curlpp::unescape(raw)); + target = resp.owned_.back(); + } else { + target = raw; + } + }; text = root.node().select_node("Name/text()"); resp.name = text.node().value(); @@ -179,8 +193,8 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, resp.encoding_type = text.node().value(); text = root.node().select_node("Prefix/text()"); - value = text.node().value(); - resp.prefix = (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + assign_unescaped(resp.encoding_type, raw, resp.prefix); text = root.node().select_node("Delimiter/text()"); resp.delimiter = text.node().value(); @@ -196,14 +210,12 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, // ListBucketResult V1 { text = root.node().select_node("Marker/text()"); - value = text.node().value(); - resp.marker = - (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + assign_unescaped(resp.encoding_type, raw, resp.marker); text = root.node().select_node("NextMarker/text()"); - value = text.node().value(); - resp.next_marker = - (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + assign_unescaped(resp.encoding_type, raw, resp.next_marker); } // ListBucketResult V2 @@ -214,9 +226,8 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, resp.key_count = static_cast(std::stoul(value)); text = root.node().select_node("StartAfter/text()"); - value = text.node().value(); - resp.start_after = - (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + assign_unescaped(resp.encoding_type, raw, resp.start_after); text = root.node().select_node("ContinuationToken/text()"); resp.continuation_token = text.node().value(); @@ -228,14 +239,12 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, // ListVersionsResult { text = root.node().select_node("KeyMarker/text()"); - value = text.node().value(); - resp.key_marker = - (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + assign_unescaped(resp.encoding_type, raw, resp.key_marker); text = root.node().select_node("NextKeyMarker/text()"); - value = text.node().value(); - resp.next_key_marker = - (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + assign_unescaped(resp.encoding_type, raw, resp.next_key_marker); text = root.node().select_node("VersionIdMarker/text()"); resp.version_id_marker = text.node().value(); @@ -249,18 +258,24 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, auto populate = [&resp = resp, &last_item = last_item]( std::list& items, pugi::xpath_node_set& contents, bool is_delete_marker) -> void { + const char* raw = nullptr; for (auto content : contents) { pugi::xpath_node text; std::string value; Item item; text = content.node().select_node("ETag/text()"); - item.etag = utils::Trim(text.node().value(), '"'); + resp.owned_.emplace_back(utils::Trim(text.node().value(), '"')); + item.etag = resp.owned_.back(); text = content.node().select_node("Key/text()"); - value = text.node().value(); - item.name = - (resp.encoding_type == "url") ? curlpp::unescape(value) : value; + raw = text.node().value(); + if (resp.encoding_type == "url") { + resp.owned_.emplace_back(curlpp::unescape(raw)); + } else { + resp.owned_.emplace_back(raw); + } + item.name = resp.owned_.back(); text = content.node().select_node("LastModified/text()"); value = text.node().value(); @@ -303,17 +318,21 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, populate(resp.contents, contents, false); // Only for ListObjectsV1. if (resp.is_truncated && resp.next_marker.empty()) { - resp.next_marker = last_item.name; + resp.owned_.emplace_back(last_item.name); + resp.next_marker = resp.owned_.back(); } auto common_prefixes = root.node().select_nodes("CommonPrefixes"); for (auto common_prefix : common_prefixes) { Item item; - text = common_prefix.node().select_node("Prefix/text()"); - value = text.node().value(); - item.name = (resp.encoding_type == "url") ? curlpp::unescape(value) : value; - + raw = text.node().value(); + if (resp.encoding_type == "url") { + resp.owned_.emplace_back(curlpp::unescape(raw)); + } else { + resp.owned_.emplace_back(raw); + } + item.name = resp.owned_.back(); item.is_prefix = true; resp.contents.push_back(item); From e87ae8e1d6e79a4dcbe62c6e4fc65fe08b13c2bb Mon Sep 17 00:00:00 2001 From: jiuker Date: Tue, 16 Jun 2026 19:25:56 +0800 Subject: [PATCH 2/3] simple --- src/response.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/response.cc b/src/response.cc index 9ea7aaa..349fad6 100644 --- a/src/response.cc +++ b/src/response.cc @@ -272,10 +272,10 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, raw = text.node().value(); if (resp.encoding_type == "url") { resp.owned_.emplace_back(curlpp::unescape(raw)); + item.name = resp.owned_.back(); } else { - resp.owned_.emplace_back(raw); + item.name = raw; } - item.name = resp.owned_.back(); text = content.node().select_node("LastModified/text()"); value = text.node().value(); @@ -329,10 +329,10 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, raw = text.node().value(); if (resp.encoding_type == "url") { resp.owned_.emplace_back(curlpp::unescape(raw)); + item.name = resp.owned_.back(); } else { - resp.owned_.emplace_back(raw); + item.name = raw; } - item.name = resp.owned_.back(); item.is_prefix = true; resp.contents.push_back(item); From e70ec6938d0dec65071386974bf62c383bff7b09 Mon Sep 17 00:00:00 2001 From: jiuker Date: Tue, 16 Jun 2026 19:46:25 +0800 Subject: [PATCH 3/3] fix --- include/miniocpp/response.h | 2 ++ src/response.cc | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/miniocpp/response.h b/include/miniocpp/response.h index 510f823..bf89166 100644 --- a/include/miniocpp/response.h +++ b/include/miniocpp/response.h @@ -20,8 +20,10 @@ #include #include +#include #include #include +#include #include #include "error.h" diff --git a/src/response.cc b/src/response.cc index 349fad6..05a48fb 100644 --- a/src/response.cc +++ b/src/response.cc @@ -54,7 +54,7 @@ Response Response::ParseXML(std::string_view data, int status_code, resp.headers = headers; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { resp.err_ = error::Error("unable to parse XML; " + std::string(data)); return resp; @@ -91,7 +91,7 @@ ListBucketsResponse ListBucketsResponse::ParseXML(std::string_view data) { std::list buckets; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -119,7 +119,7 @@ CompleteMultipartUploadResponse CompleteMultipartUploadResponse::ParseXML( CompleteMultipartUploadResponse resp; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -161,7 +161,7 @@ ListObjectsResponse ListObjectsResponse::ParseXML(std::string_view data, ListObjectsResponse resp; auto xdoc = std::make_shared(); - pugi::xml_parse_result result = xdoc->load_string(data.data()); + pugi::xml_parse_result result = xdoc->load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -348,7 +348,7 @@ RemoveObjectsResponse RemoveObjectsResponse::ParseXML(std::string_view data) { RemoveObjectsResponse resp; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -405,7 +405,7 @@ GetBucketNotificationResponse GetBucketNotificationResponse::ParseXML( NotificationConfig config; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -500,7 +500,7 @@ GetBucketEncryptionResponse GetBucketEncryptionResponse::ParseXML( SseConfig config; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -522,7 +522,7 @@ GetBucketReplicationResponse GetBucketReplicationResponse::ParseXML( ReplicationConfig config; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -688,7 +688,7 @@ GetBucketLifecycleResponse GetBucketLifecycleResponse::ParseXML( LifecycleConfig config; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -815,7 +815,7 @@ GetBucketTagsResponse GetBucketTagsResponse::ParseXML(std::string_view data) { std::map map; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); } @@ -840,7 +840,7 @@ GetObjectTagsResponse GetObjectTagsResponse::ParseXML(std::string_view data) { std::map map; pugi::xml_document xdoc; - pugi::xml_parse_result result = xdoc.load_string(data.data()); + pugi::xml_parse_result result = xdoc.load_buffer(data.data(), data.size()); if (!result) { return error::make("unable to parse XML"); }