New XML parser on Windows (#2830)
This commit is contained in:
parent
b0ebf21a9f
commit
47d04c3ff9
@ -47,7 +47,7 @@ For general suggestions about Azure, use our [Azure feedback forum](https://feed
|
||||
On Windows, dependencies are managed by [vcpkg](https://github.com/microsoft/vcpkg). You can reference the [Quick Start](https://github.com/microsoft/vcpkg#quick-start-windows) to quickly set yourself up.
|
||||
After Vcpkg is initialized and bootstrapped, you can install the dependencies:
|
||||
```BatchFile
|
||||
vcpkg.exe install libxml2:x64-windows-static curl:x64-windows-static
|
||||
vcpkg.exe install curl:x64-windows-static
|
||||
```
|
||||
|
||||
#### POSIX Platforms
|
||||
|
||||
@ -1133,10 +1133,11 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
{
|
||||
const std::string non_ascii_word = "\xE6\xB5\x8B\xE8\xAF\x95";
|
||||
const std::string encoded_non_ascii_word = "%E6%B5%8B%E8%AF%95";
|
||||
std::string baseBlobName = "a b c / !@#$%^&*(?/<>,.;:'\"[]{}|`~\\) def" + non_ascii_word;
|
||||
ASSERT_EQ(_internal::UrlEncodePath(non_ascii_word), encoded_non_ascii_word);
|
||||
const std::string baseBlobName = "a b c / !@#$%^&*(?/<>,.;:'\"[]{}|`~\\) def" + non_ascii_word;
|
||||
|
||||
{
|
||||
std::string blobName = baseBlobName + RandomString();
|
||||
const std::string blobName = baseBlobName + RandomString();
|
||||
auto blobClient = m_blobContainerClient->GetAppendBlobClient(blobName);
|
||||
EXPECT_NO_THROW(blobClient.Create());
|
||||
auto blobUrl = blobClient.GetUrl();
|
||||
@ -1144,7 +1145,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName));
|
||||
}
|
||||
{
|
||||
std::string blobName = baseBlobName + RandomString();
|
||||
const std::string blobName = baseBlobName + RandomString();
|
||||
auto blobClient = m_blobContainerClient->GetPageBlobClient(blobName);
|
||||
EXPECT_NO_THROW(blobClient.Create(1024));
|
||||
auto blobUrl = blobClient.GetUrl();
|
||||
@ -1152,16 +1153,15 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName));
|
||||
}
|
||||
{
|
||||
std::string blobName = baseBlobName + RandomString();
|
||||
const std::string blobName = baseBlobName + RandomString();
|
||||
auto blobClient = m_blobContainerClient->GetBlockBlobClient(blobName);
|
||||
EXPECT_NO_THROW(blobClient.UploadFrom(nullptr, 0));
|
||||
auto blobUrl = blobClient.GetUrl();
|
||||
EXPECT_EQ(
|
||||
blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName));
|
||||
}
|
||||
|
||||
{
|
||||
std::string blobName = baseBlobName + RandomString();
|
||||
const std::string blobName = baseBlobName + RandomString();
|
||||
auto blobClient = Blobs::AppendBlobClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), m_containerName, blobName);
|
||||
EXPECT_NO_THROW(blobClient.Create());
|
||||
@ -1170,7 +1170,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName));
|
||||
}
|
||||
{
|
||||
std::string blobName = baseBlobName + RandomString();
|
||||
const std::string blobName = baseBlobName + RandomString();
|
||||
auto blobClient = Blobs::PageBlobClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), m_containerName, blobName);
|
||||
EXPECT_NO_THROW(blobClient.Create(1024));
|
||||
@ -1179,7 +1179,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
blobUrl, m_blobContainerClient->GetUrl() + "/" + _internal::UrlEncodePath(blobName));
|
||||
}
|
||||
{
|
||||
std::string blobName = baseBlobName + RandomString();
|
||||
const std::string blobName = baseBlobName + RandomString();
|
||||
auto blobClient = Blobs::BlockBlobClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), m_containerName, blobName);
|
||||
EXPECT_NO_THROW(blobClient.UploadFrom(nullptr, 0));
|
||||
|
||||
@ -4,10 +4,14 @@
|
||||
|
||||
### Features Added
|
||||
|
||||
- Used new xml library on Windows, dropped dependency for libxml2.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
- Fixed a bug that may cause crash when parsing XML.
|
||||
|
||||
### Other Changes
|
||||
|
||||
## 12.1.0 (2021-08-10)
|
||||
|
||||
@ -27,7 +27,6 @@ if(NOT AZ_ALL_LIBRARIES)
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(LibXml2 REQUIRED)
|
||||
|
||||
set(
|
||||
AZURE_STORAGE_COMMON_HEADER
|
||||
@ -78,14 +77,15 @@ target_include_directories(
|
||||
)
|
||||
|
||||
target_link_libraries(azure-storage-common PUBLIC Azure::azure-core)
|
||||
target_include_directories(azure-storage-common PRIVATE ${LIBXML2_INCLUDE_DIRS})
|
||||
target_link_libraries(azure-storage-common PRIVATE ${LIBXML2_LIBRARIES})
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(azure-storage-common PRIVATE bcrypt)
|
||||
target_link_libraries(azure-storage-common PRIVATE bcrypt webservices)
|
||||
# C28020 and C28204 are introduced by nlohmann/json
|
||||
target_compile_options(azure-storage-common PUBLIC /wd28204 /wd28020)
|
||||
else()
|
||||
find_package(LibXml2 REQUIRED)
|
||||
target_include_directories(azure-storage-common PRIVATE ${LIBXML2_INCLUDE_DIRS})
|
||||
target_link_libraries(azure-storage-common PRIVATE ${LIBXML2_LIBRARIES})
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(azure-storage-common PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
endif()
|
||||
|
||||
@ -36,18 +36,35 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
class XmlReader final {
|
||||
public:
|
||||
explicit XmlReader(const char* data, size_t length);
|
||||
XmlReader(const XmlReader& other) = delete;
|
||||
XmlReader& operator=(const XmlReader& other) = delete;
|
||||
XmlReader(XmlReader&& other) noexcept { *this = std::move(other); }
|
||||
XmlReader& operator=(XmlReader&& other) noexcept
|
||||
{
|
||||
m_context = other.m_context;
|
||||
other.m_context = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~XmlReader();
|
||||
|
||||
XmlNode Read();
|
||||
|
||||
private:
|
||||
void* m_reader = nullptr;
|
||||
bool m_readingAttributes = false;
|
||||
void* m_context = nullptr;
|
||||
};
|
||||
|
||||
class XmlWriter final {
|
||||
public:
|
||||
explicit XmlWriter();
|
||||
XmlWriter(const XmlWriter& other) = delete;
|
||||
XmlWriter& operator=(const XmlWriter& other) = delete;
|
||||
XmlWriter(XmlWriter&& other) noexcept { *this = std::move(other); }
|
||||
XmlWriter& operator=(XmlWriter&& other) noexcept
|
||||
{
|
||||
m_context = other.m_context;
|
||||
other.m_context = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~XmlWriter();
|
||||
|
||||
void Write(XmlNode node);
|
||||
@ -55,8 +72,7 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
std::string GetDocument();
|
||||
|
||||
private:
|
||||
void* m_buffer = nullptr;
|
||||
void* m_writer = nullptr;
|
||||
void* m_context = nullptr;
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Storage::_internal
|
||||
|
||||
@ -4,13 +4,381 @@
|
||||
#include "azure/storage/common/internal/xml_wrapper.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <azure/core/platform.hpp>
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <webservices.h>
|
||||
#else
|
||||
#include <libxml/xmlreader.h>
|
||||
#include <libxml/xmlwriter.h>
|
||||
#endif
|
||||
|
||||
namespace Azure { namespace Storage { namespace _internal {
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
|
||||
struct XmlReaderContext
|
||||
{
|
||||
XmlReaderContext()
|
||||
{
|
||||
HRESULT ret = WsCreateError(nullptr, 0, &error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize xml reader.");
|
||||
}
|
||||
ret = WsCreateReader(nullptr, 0, &reader, error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
WsFreeError(error);
|
||||
throw std::runtime_error("Failed to initialize xml reader.");
|
||||
}
|
||||
}
|
||||
XmlReaderContext(const XmlReaderContext&) = delete;
|
||||
XmlReaderContext& operator=(const XmlReaderContext&) = delete;
|
||||
~XmlReaderContext()
|
||||
{
|
||||
WsFreeReader(reader);
|
||||
WsFreeError(error);
|
||||
}
|
||||
|
||||
WS_XML_READER* reader = nullptr;
|
||||
WS_ERROR* error = nullptr;
|
||||
bool readingAttributes = false;
|
||||
ULONG attributeIndex = 0;
|
||||
const WS_XML_ELEMENT_NODE* attributeElementNode = nullptr;
|
||||
};
|
||||
|
||||
XmlReader::XmlReader(const char* data, size_t length)
|
||||
{
|
||||
if (length > static_cast<size_t>(std::numeric_limits<ULONG>::max()))
|
||||
{
|
||||
throw std::runtime_error("Xml data too big.");
|
||||
}
|
||||
|
||||
auto context = std::make_unique<XmlReaderContext>();
|
||||
|
||||
WS_XML_READER_BUFFER_INPUT bufferInput;
|
||||
ZeroMemory(&bufferInput, sizeof(bufferInput));
|
||||
bufferInput.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
|
||||
bufferInput.encodedData = const_cast<char*>(data);
|
||||
bufferInput.encodedDataSize = static_cast<ULONG>(length);
|
||||
WS_XML_READER_TEXT_ENCODING textEncoding;
|
||||
ZeroMemory(&textEncoding, sizeof(textEncoding));
|
||||
textEncoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT;
|
||||
textEncoding.charSet = WS_CHARSET_AUTO;
|
||||
HRESULT ret = WsSetInput(
|
||||
context->reader, &textEncoding.encoding, &bufferInput.input, nullptr, 0, context->error);
|
||||
if (ret != S_OK)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize xml reader.");
|
||||
}
|
||||
|
||||
WS_CHARSET charSet;
|
||||
ret = WsGetReaderProperty(
|
||||
context->reader, WS_XML_READER_PROPERTY_CHARSET, &charSet, sizeof(charSet), context->error);
|
||||
if (ret != S_OK)
|
||||
{
|
||||
throw std::runtime_error("Failed to get xml encoding.");
|
||||
}
|
||||
if (charSet != WS_CHARSET_UTF8)
|
||||
{
|
||||
throw std::runtime_error("Unsupported xml encoding.");
|
||||
}
|
||||
|
||||
m_context = context.release();
|
||||
}
|
||||
|
||||
XmlReader::~XmlReader()
|
||||
{
|
||||
if (m_context)
|
||||
{
|
||||
delete static_cast<XmlReaderContext*>(m_context);
|
||||
}
|
||||
}
|
||||
|
||||
XmlNode XmlReader::Read()
|
||||
{
|
||||
auto context = static_cast<XmlReaderContext*>(m_context);
|
||||
|
||||
if (context->readingAttributes)
|
||||
{
|
||||
const WS_XML_ATTRIBUTE* attribute
|
||||
= context->attributeElementNode->attributes[context->attributeIndex++];
|
||||
if (context->attributeIndex == context->attributeElementNode->attributeCount)
|
||||
{
|
||||
context->readingAttributes = false;
|
||||
context->attributeElementNode = nullptr;
|
||||
context->attributeIndex = 0;
|
||||
}
|
||||
|
||||
std::string name(
|
||||
reinterpret_cast<const char*>(attribute->localName->bytes), attribute->localName->length);
|
||||
|
||||
if (attribute->value->textType != WS_XML_TEXT_TYPE_UTF8)
|
||||
{
|
||||
throw std::runtime_error("Unsupported xml encoding.");
|
||||
}
|
||||
|
||||
const WS_XML_UTF8_TEXT* utf8Text
|
||||
= reinterpret_cast<const WS_XML_UTF8_TEXT*>(attribute->value);
|
||||
std::string value(
|
||||
reinterpret_cast<const char*>(utf8Text->value.bytes), utf8Text->value.length);
|
||||
|
||||
return XmlNode{XmlNodeType::Attribute, std::move(name), std::move(value)};
|
||||
}
|
||||
|
||||
auto moveToNext = [&]() {
|
||||
HRESULT ret = WsReadNode(context->reader, context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to parse xml.");
|
||||
}
|
||||
};
|
||||
// This will skip WS_XML_NODE_TYPE_BOF when parsing the very beginning of an xml document. But
|
||||
// it's fine. We don't care about that xml node anyway.
|
||||
moveToNext();
|
||||
|
||||
const WS_XML_NODE* node;
|
||||
HRESULT ret = WsGetReaderNode(context->reader, &node, context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to parse xml.");
|
||||
}
|
||||
switch (node->nodeType)
|
||||
{
|
||||
case WS_XML_NODE_TYPE_ELEMENT: {
|
||||
const WS_XML_ELEMENT_NODE* elementNode = reinterpret_cast<const WS_XML_ELEMENT_NODE*>(node);
|
||||
std::string name(
|
||||
reinterpret_cast<const char*>(elementNode->localName->bytes),
|
||||
elementNode->localName->length);
|
||||
if (elementNode->attributeCount != 0)
|
||||
{
|
||||
context->readingAttributes = true;
|
||||
context->attributeElementNode = elementNode;
|
||||
context->attributeIndex = 0;
|
||||
}
|
||||
if (elementNode->isEmpty)
|
||||
{
|
||||
// Skip the end tag.
|
||||
moveToNext();
|
||||
return XmlNode{XmlNodeType::SelfClosingTag, std::move(name)};
|
||||
}
|
||||
else
|
||||
{
|
||||
return XmlNode{XmlNodeType::StartTag, std::move(name)};
|
||||
}
|
||||
}
|
||||
case WS_XML_NODE_TYPE_TEXT: {
|
||||
const WS_XML_TEXT_NODE* textNode = (const WS_XML_TEXT_NODE*)node;
|
||||
if (textNode->text->textType != WS_XML_TEXT_TYPE_UTF8)
|
||||
{
|
||||
throw std::runtime_error("Unsupported xml encoding.");
|
||||
}
|
||||
const WS_XML_UTF8_TEXT* utf8Text
|
||||
= reinterpret_cast<const WS_XML_UTF8_TEXT*>(textNode->text);
|
||||
std::string value(
|
||||
reinterpret_cast<const char*>(utf8Text->value.bytes), utf8Text->value.length);
|
||||
return XmlNode{XmlNodeType::Text, std::string(), std::move(value)};
|
||||
}
|
||||
case WS_XML_NODE_TYPE_END_ELEMENT:
|
||||
return XmlNode{XmlNodeType::EndTag, std::string()};
|
||||
case WS_XML_NODE_TYPE_EOF:
|
||||
return XmlNode{XmlNodeType::End};
|
||||
case WS_XML_NODE_TYPE_CDATA:
|
||||
case WS_XML_NODE_TYPE_END_CDATA:
|
||||
case WS_XML_NODE_TYPE_COMMENT:
|
||||
case WS_XML_NODE_TYPE_BOF:
|
||||
return Read();
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
"Unknown type " + std::to_string(node->nodeType) + " while parsing xml.");
|
||||
}
|
||||
}
|
||||
|
||||
struct XmlWriterContext
|
||||
{
|
||||
XmlWriterContext()
|
||||
{
|
||||
HRESULT ret = WsCreateError(nullptr, 0, &error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
ret = WsCreateWriter(nullptr, 0, &writer, error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
WsFreeError(error);
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
ret = WsCreateHeap(1024 * 1024 * 1024, 512, nullptr, 0, &heap, error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
WsFreeWriter(writer);
|
||||
WsFreeError(error);
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
}
|
||||
XmlWriterContext(const XmlWriterContext&) = delete;
|
||||
XmlWriterContext& operator=(const XmlWriterContext&) = delete;
|
||||
~XmlWriterContext()
|
||||
{
|
||||
WsFreeHeap(heap);
|
||||
WsFreeWriter(writer);
|
||||
WsFreeError(error);
|
||||
}
|
||||
|
||||
WS_XML_WRITER* writer = nullptr;
|
||||
WS_ERROR* error = nullptr;
|
||||
WS_HEAP* heap = nullptr;
|
||||
WS_XML_BUFFER* buffer = nullptr;
|
||||
};
|
||||
|
||||
XmlWriter::XmlWriter()
|
||||
{
|
||||
auto context = std::make_unique<XmlWriterContext>();
|
||||
|
||||
HRESULT ret = WsCreateXmlBuffer(context->heap, nullptr, 0, &context->buffer, context->error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
|
||||
ret = WsSetOutputToBuffer(context->writer, context->buffer, nullptr, 0, context->error);
|
||||
if (ret != NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
|
||||
m_context = context.release();
|
||||
}
|
||||
|
||||
XmlWriter::~XmlWriter()
|
||||
{
|
||||
if (m_context)
|
||||
{
|
||||
delete static_cast<XmlWriterContext*>(m_context);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWriter::Write(XmlNode node)
|
||||
{
|
||||
auto context = static_cast<XmlWriterContext*>(m_context);
|
||||
if (node.Type == XmlNodeType::StartTag)
|
||||
{
|
||||
if (!node.Value.empty())
|
||||
{
|
||||
Write(XmlNode{XmlNodeType::StartTag, std::move(node.Name)});
|
||||
Write(XmlNode{XmlNodeType::Text, std::string(), std::move(node.Value)});
|
||||
Write(XmlNode{XmlNodeType::EndTag});
|
||||
return;
|
||||
}
|
||||
WS_XML_STRING name;
|
||||
name.bytes = reinterpret_cast<BYTE*>(&node.Name[0]);
|
||||
name.length = static_cast<ULONG>(node.Name.length());
|
||||
name.dictionary = nullptr;
|
||||
WS_XML_STRING ns = WS_XML_STRING_NULL;
|
||||
HRESULT ret = WsWriteStartElement(context->writer, nullptr, &name, &ns, context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to write xml.");
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::EndTag)
|
||||
{
|
||||
HRESULT ret = WsWriteEndElement(context->writer, context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to write xml.");
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::SelfClosingTag)
|
||||
{
|
||||
Write(XmlNode{XmlNodeType::StartTag, std::move(node.Name), std::move(node.Value)});
|
||||
Write(XmlNode{XmlNodeType::EndTag});
|
||||
}
|
||||
else if (node.Type == XmlNodeType::Text)
|
||||
{
|
||||
HRESULT ret = WsWriteCharsUtf8(
|
||||
context->writer,
|
||||
reinterpret_cast<const BYTE*>(node.Value.data()),
|
||||
static_cast<ULONG>(node.Value.size()),
|
||||
context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to write xml.");
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::Attribute)
|
||||
{
|
||||
WS_XML_STRING name;
|
||||
name.bytes = reinterpret_cast<BYTE*>(&node.Name[0]);
|
||||
name.length = static_cast<ULONG>(node.Name.length());
|
||||
name.dictionary = nullptr;
|
||||
WS_XML_STRING ns = WS_XML_STRING_NULL;
|
||||
HRESULT ret
|
||||
= WsWriteStartAttribute(context->writer, nullptr, &name, &ns, FALSE, context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to write xml.");
|
||||
}
|
||||
Write(XmlNode{XmlNodeType::Text, std::string(), std::move(node.Value)});
|
||||
ret = WsWriteEndAttribute(context->writer, context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to write xml.");
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::End)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Unsupported XmlNode type "
|
||||
+ std::to_string(static_cast<std::underlying_type<XmlNodeType>::type>(node.Type)) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
std::string XmlWriter::GetDocument()
|
||||
{
|
||||
auto context = static_cast<XmlWriterContext*>(m_context);
|
||||
|
||||
BOOL boolValueTrue = TRUE;
|
||||
WS_XML_WRITER_PROPERTY writerProperty;
|
||||
writerProperty.id = WS_XML_WRITER_PROPERTY_WRITE_DECLARATION;
|
||||
writerProperty.value = &boolValueTrue;
|
||||
writerProperty.valueSize = sizeof(boolValueTrue);
|
||||
void* xml = nullptr;
|
||||
ULONG xmlLength = 0;
|
||||
HRESULT ret = WsWriteXmlBufferToBytes(
|
||||
context->writer,
|
||||
context->buffer,
|
||||
nullptr,
|
||||
&writerProperty,
|
||||
1,
|
||||
context->heap,
|
||||
&xml,
|
||||
&xmlLength,
|
||||
context->error);
|
||||
if (!SUCCEEDED(ret))
|
||||
{
|
||||
throw std::runtime_error("Failed to write xml.");
|
||||
}
|
||||
|
||||
return std::string(static_cast<const char*>(xml), xmlLength);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct XmlGlobalInitializer final
|
||||
{
|
||||
XmlGlobalInitializer() { xmlInitParser(); }
|
||||
@ -19,6 +387,12 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
|
||||
static void XmlGlobalInitialize() { static XmlGlobalInitializer globalInitializer; }
|
||||
|
||||
struct XmlReaderContext
|
||||
{
|
||||
xmlTextReaderPtr reader = nullptr;
|
||||
bool readingAttributes = false;
|
||||
};
|
||||
|
||||
XmlReader::XmlReader(const char* data, size_t length)
|
||||
{
|
||||
XmlGlobalInitialize();
|
||||
@ -28,30 +402,43 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
throw std::runtime_error("Xml data too big.");
|
||||
}
|
||||
|
||||
m_reader = xmlReaderForMemory(data, static_cast<int>(length), nullptr, nullptr, 0);
|
||||
if (!m_reader)
|
||||
xmlTextReaderPtr reader
|
||||
= xmlReaderForMemory(data, static_cast<int>(length), nullptr, nullptr, 0);
|
||||
if (!reader)
|
||||
{
|
||||
throw std::runtime_error("Failed to parse xml.");
|
||||
}
|
||||
|
||||
XmlReaderContext* context = new XmlReaderContext();
|
||||
context->reader = reader;
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
XmlReader::~XmlReader() { xmlFreeTextReader(static_cast<xmlTextReaderPtr>(m_reader)); }
|
||||
XmlReader::~XmlReader()
|
||||
{
|
||||
if (m_context)
|
||||
{
|
||||
auto context = static_cast<XmlReaderContext*>(m_context);
|
||||
xmlFreeTextReader(static_cast<xmlTextReaderPtr>(context->reader));
|
||||
delete context;
|
||||
}
|
||||
}
|
||||
|
||||
XmlNode XmlReader::Read()
|
||||
{
|
||||
xmlTextReaderPtr reader = static_cast<xmlTextReaderPtr>(m_reader);
|
||||
if (m_readingAttributes)
|
||||
auto context = static_cast<XmlReaderContext*>(m_context);
|
||||
if (context->readingAttributes)
|
||||
{
|
||||
int ret = xmlTextReaderMoveToNextAttribute(reader);
|
||||
int ret = xmlTextReaderMoveToNextAttribute(context->reader);
|
||||
if (ret == 1)
|
||||
{
|
||||
const char* name = reinterpret_cast<const char*>(xmlTextReaderConstName(reader));
|
||||
const char* value = reinterpret_cast<const char*>(xmlTextReaderConstValue(reader));
|
||||
const char* name = reinterpret_cast<const char*>(xmlTextReaderConstName(context->reader));
|
||||
const char* value = reinterpret_cast<const char*>(xmlTextReaderConstValue(context->reader));
|
||||
return XmlNode{XmlNodeType::Attribute, name, value};
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
m_readingAttributes = false;
|
||||
context->readingAttributes = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -59,7 +446,7 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
}
|
||||
}
|
||||
|
||||
int ret = xmlTextReaderRead(reader);
|
||||
int ret = xmlTextReaderRead(context->reader);
|
||||
if (ret == 0)
|
||||
{
|
||||
return XmlNode{XmlNodeType::End};
|
||||
@ -69,17 +456,17 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
throw std::runtime_error("Failed to parse xml.");
|
||||
}
|
||||
|
||||
int type = xmlTextReaderNodeType(reader);
|
||||
bool is_empty = xmlTextReaderIsEmptyElement(reader) == 1;
|
||||
bool has_value = xmlTextReaderHasValue(reader) == 1;
|
||||
bool has_attributes = xmlTextReaderHasAttributes(reader) == 1;
|
||||
int type = xmlTextReaderNodeType(context->reader);
|
||||
bool is_empty = xmlTextReaderIsEmptyElement(context->reader) == 1;
|
||||
bool has_value = xmlTextReaderHasValue(context->reader) == 1;
|
||||
bool has_attributes = xmlTextReaderHasAttributes(context->reader) == 1;
|
||||
|
||||
const char* name = reinterpret_cast<const char*>(xmlTextReaderConstName(reader));
|
||||
const char* value = reinterpret_cast<const char*>(xmlTextReaderConstValue(reader));
|
||||
const char* name = reinterpret_cast<const char*>(xmlTextReaderConstName(context->reader));
|
||||
const char* value = reinterpret_cast<const char*>(xmlTextReaderConstValue(context->reader));
|
||||
|
||||
if (has_attributes)
|
||||
{
|
||||
m_readingAttributes = true;
|
||||
context->readingAttributes = true;
|
||||
}
|
||||
|
||||
if (type == XML_READER_TYPE_ELEMENT && is_empty)
|
||||
@ -113,18 +500,47 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
return Read();
|
||||
}
|
||||
|
||||
struct XmlWriterContext
|
||||
{
|
||||
xmlBufferPtr buffer;
|
||||
xmlTextWriterPtr writer;
|
||||
};
|
||||
|
||||
XmlWriter::XmlWriter()
|
||||
{
|
||||
XmlGlobalInitialize();
|
||||
m_buffer = xmlBufferCreate();
|
||||
m_writer = xmlNewTextWriterMemory(static_cast<xmlBufferPtr>(m_buffer), 0);
|
||||
xmlTextWriterStartDocument(static_cast<xmlTextWriterPtr>(m_writer), nullptr, nullptr, nullptr);
|
||||
auto buffer = xmlBufferCreate();
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
|
||||
auto writer = xmlNewTextWriterMemory(static_cast<xmlBufferPtr>(buffer), 0);
|
||||
|
||||
if (!writer)
|
||||
{
|
||||
xmlBufferFree(static_cast<xmlBufferPtr>(buffer));
|
||||
throw std::runtime_error("Failed to initialize xml writer.");
|
||||
}
|
||||
|
||||
xmlTextWriterStartDocument(static_cast<xmlTextWriterPtr>(writer), nullptr, nullptr, nullptr);
|
||||
|
||||
auto context = new XmlWriterContext;
|
||||
context->buffer = buffer;
|
||||
context->writer = writer;
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
XmlWriter::~XmlWriter()
|
||||
{
|
||||
xmlFreeTextWriter(static_cast<xmlTextWriterPtr>(m_writer));
|
||||
xmlBufferFree(static_cast<xmlBufferPtr>(m_buffer));
|
||||
if (m_context)
|
||||
{
|
||||
auto context = static_cast<XmlWriterContext*>(m_context);
|
||||
xmlFreeTextWriter(static_cast<xmlTextWriterPtr>(context->writer));
|
||||
xmlBufferFree(static_cast<xmlBufferPtr>(context->buffer));
|
||||
delete context;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -136,7 +552,8 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
|
||||
void XmlWriter::Write(XmlNode node)
|
||||
{
|
||||
xmlTextWriterPtr writer = static_cast<xmlTextWriterPtr>(m_writer);
|
||||
auto context = static_cast<XmlWriterContext*>(m_context);
|
||||
xmlTextWriterPtr writer = context->writer;
|
||||
if (node.Type == XmlNodeType::StartTag)
|
||||
{
|
||||
if (node.Value.empty())
|
||||
@ -179,10 +596,13 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
|
||||
std::string XmlWriter::GetDocument()
|
||||
{
|
||||
xmlTextWriterPtr writer = static_cast<xmlTextWriterPtr>(m_writer);
|
||||
xmlBufferPtr buffer = static_cast<xmlBufferPtr>(m_buffer);
|
||||
auto context = static_cast<XmlWriterContext*>(m_context);
|
||||
xmlTextWriterPtr writer = context->writer;
|
||||
xmlBufferPtr buffer = context->buffer;
|
||||
xmlTextWriterFlush(writer);
|
||||
return std::string(reinterpret_cast<const char*>(buffer->content), buffer->use);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}}} // namespace Azure::Storage::_internal
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(LibXml2)
|
||||
find_dependency(Threads)
|
||||
find_dependency(azure-core-cpp "1.0.0")
|
||||
|
||||
if(NOT WIN32)
|
||||
find_dependency(LibXml2)
|
||||
find_dependency(OpenSSL)
|
||||
endif()
|
||||
|
||||
|
||||
@ -16,7 +16,10 @@
|
||||
"default-features": false,
|
||||
"version>=": "1.0.0"
|
||||
},
|
||||
"libxml2",
|
||||
{
|
||||
"name": "libxml2",
|
||||
"platform": "!windows"
|
||||
}
|
||||
{
|
||||
"name": "openssl",
|
||||
"platform": "!windows"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user