|  | #include "gzip_utils.hpp" | 
|  |  | 
|  | #include <libxml/parser.h> | 
|  | #include <libxml/xpath.h> | 
|  | #include <unistd.h> | 
|  | #include <zlib.h> | 
|  |  | 
|  | #include <optional> | 
|  | #include <span> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | std::optional<std::string> gzipInflate(std::span<uint8_t> compressedBytes) | 
|  | { | 
|  | std::string uncompressedBytes; | 
|  | if (compressedBytes.empty()) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | z_stream strm{ | 
|  |  | 
|  | }; | 
|  | strm.next_in = (Bytef*)compressedBytes.data(); | 
|  | strm.avail_in = static_cast<uInt>(compressedBytes.size()); | 
|  | strm.total_out = 0; | 
|  |  | 
|  | if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | while (strm.avail_in > 0) | 
|  | { | 
|  | constexpr size_t chunkSize = 1024; | 
|  | uncompressedBytes.resize(uncompressedBytes.size() + chunkSize); | 
|  | strm.next_out = | 
|  | std::bit_cast<Bytef*>(uncompressedBytes.end() - chunkSize); | 
|  | strm.avail_out = chunkSize; | 
|  |  | 
|  | // Inflate another chunk. | 
|  | int err = inflate(&strm, Z_SYNC_FLUSH); | 
|  | if (err == Z_STREAM_END) | 
|  | { | 
|  | break; | 
|  | } | 
|  | if (err != Z_OK) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (inflateEnd(&strm) != Z_OK) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  | uncompressedBytes.resize(strm.total_out); | 
|  |  | 
|  | return {uncompressedBytes}; | 
|  | } | 
|  |  | 
|  | static std::vector<std::string> xpathText(xmlDocPtr doc, const char* xp) | 
|  | { | 
|  | std::vector<std::string> val; | 
|  | xmlXPathContextPtr ctx = xmlXPathNewContext(doc); | 
|  | if (ctx == nullptr) | 
|  | { | 
|  | return val; | 
|  | } | 
|  | const unsigned char* xpptr = std::bit_cast<const unsigned char*>(xp); | 
|  | xmlXPathObjectPtr obj = xmlXPathEvalExpression(xpptr, ctx); | 
|  | if (obj != nullptr) | 
|  | { | 
|  | if (obj->type == XPATH_NODESET && obj->nodesetval != nullptr) | 
|  | { | 
|  | xmlNodeSetPtr nodeTab = obj->nodesetval; | 
|  | size_t nodeNr = static_cast<size_t>(nodeTab->nodeNr); | 
|  | std::span<xmlNodePtr> nodes{nodeTab->nodeTab, nodeNr}; | 
|  | for (xmlNodePtr node : nodes) | 
|  | { | 
|  | unsigned char* keyword = xmlNodeGetContent(node); | 
|  | val.emplace_back(std::bit_cast<const char*>(keyword)); | 
|  | xmlFree(keyword); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | xmlXPathFreeObject(obj); | 
|  | xmlXPathFreeContext(ctx); | 
|  | return val; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> getNodeFromXml(std::string_view xml, | 
|  | const char* nodeName) | 
|  | { | 
|  | std::vector<std::string> node; | 
|  | if (xml.empty()) | 
|  | { | 
|  | return node; | 
|  | } | 
|  | xmlDocPtr doc = xmlReadMemory( | 
|  | xml.data(), xml.size(), nullptr, nullptr, | 
|  | XML_PARSE_RECOVER | XML_PARSE_NONET | XML_PARSE_NOWARNING); | 
|  | if (doc == nullptr) | 
|  | { | 
|  | return {}; | 
|  | } | 
|  | node = xpathText(doc, nodeName); | 
|  | xmlFreeDoc(doc); | 
|  | return node; | 
|  | } |