Commit afb09d2eaaed64af03021b56f2ef3923dd6fccde
Committed by
GitHub
Merge pull request #1551 from m-holger/buffer
Refactor Buffer
Showing
4 changed files
with
137 additions
and
61 deletions
include/qpdf/Buffer.hh
| @@ -25,6 +25,7 @@ | @@ -25,6 +25,7 @@ | ||
| 25 | #include <cstddef> | 25 | #include <cstddef> |
| 26 | #include <memory> | 26 | #include <memory> |
| 27 | #include <string> | 27 | #include <string> |
| 28 | +#include <string_view> | ||
| 28 | 29 | ||
| 29 | class Buffer | 30 | class Buffer |
| 30 | { | 31 | { |
| @@ -66,11 +67,34 @@ class Buffer | @@ -66,11 +67,34 @@ class Buffer | ||
| 66 | QPDF_DLL | 67 | QPDF_DLL |
| 67 | Buffer copy() const; | 68 | Buffer copy() const; |
| 68 | 69 | ||
| 70 | + // Move the content of the Buffer. After calling this method, the Buffer will be empty if the | ||
| 71 | + // buffer owns its memory. Otherwise, the Buffer will be unchanged. | ||
| 72 | + QPDF_DLL | ||
| 73 | + std::string move(); | ||
| 74 | + | ||
| 75 | + // Return a string_view to the data. | ||
| 76 | + QPDF_DLL | ||
| 77 | + std::string_view view() const; | ||
| 78 | + | ||
| 79 | + // Return a pointer to the data. NB: Unlike getBuffer, this method returns a valid pointer even | ||
| 80 | + // if the Buffer is empty. | ||
| 81 | + QPDF_DLL | ||
| 82 | + char const* data() const; | ||
| 83 | + | ||
| 84 | + // Return a pointer to the data. NB: Unlike getBuffer, this method returns a valid pointer even | ||
| 85 | + // if the Buffer is empty. | ||
| 86 | + QPDF_DLL | ||
| 87 | + char* data(); | ||
| 88 | + | ||
| 89 | + QPDF_DLL | ||
| 90 | + bool empty() const; | ||
| 91 | + | ||
| 92 | + QPDF_DLL | ||
| 93 | + size_t size() const; | ||
| 94 | + | ||
| 69 | private: | 95 | private: |
| 70 | class Members; | 96 | class Members; |
| 71 | 97 | ||
| 72 | - void copy(Buffer const&); | ||
| 73 | - | ||
| 74 | std::unique_ptr<Members> m; | 98 | std::unique_ptr<Members> m; |
| 75 | }; | 99 | }; |
| 76 | 100 |
libqpdf/Buffer.cc
| @@ -2,75 +2,55 @@ | @@ -2,75 +2,55 @@ | ||
| 2 | 2 | ||
| 3 | #include <qpdf/Buffer.hh> | 3 | #include <qpdf/Buffer.hh> |
| 4 | 4 | ||
| 5 | -#include <cstring> | ||
| 6 | - | ||
| 7 | class Buffer::Members | 5 | class Buffer::Members |
| 8 | { | 6 | { |
| 9 | friend class Buffer; | 7 | friend class Buffer; |
| 10 | 8 | ||
| 11 | public: | 9 | public: |
| 12 | - ~Members(); | ||
| 13 | - | ||
| 14 | - private: | ||
| 15 | - Members(size_t size, unsigned char* buf, bool own_memory); | ||
| 16 | - Members(std::string&& content); | 10 | + Members() = default; |
| 11 | + // Constructor for Buffers that don't own the memory. | ||
| 12 | + Members(size_t size, char* buf) : | ||
| 13 | + size(size), | ||
| 14 | + buf(buf) | ||
| 15 | + { | ||
| 16 | + } | ||
| 17 | + Members(std::string&& content) : | ||
| 18 | + str(std::move(content)), | ||
| 19 | + size(str.size()), | ||
| 20 | + buf(str.data()) | ||
| 21 | + { | ||
| 22 | + } | ||
| 17 | Members(Members const&) = delete; | 23 | Members(Members const&) = delete; |
| 24 | + ~Members() = default; | ||
| 18 | 25 | ||
| 26 | + private: | ||
| 19 | std::string str; | 27 | std::string str; |
| 20 | - bool own_memory; | ||
| 21 | size_t size; | 28 | size_t size; |
| 22 | - unsigned char* buf; | 29 | + char* buf; |
| 23 | }; | 30 | }; |
| 24 | 31 | ||
| 25 | -Buffer::Members::Members(size_t size, unsigned char* buf, bool own_memory) : | ||
| 26 | - own_memory(own_memory), | ||
| 27 | - size(size), | ||
| 28 | - buf(nullptr) | ||
| 29 | -{ | ||
| 30 | - if (own_memory) { | ||
| 31 | - this->buf = (size ? new unsigned char[size] : nullptr); | ||
| 32 | - } else { | ||
| 33 | - this->buf = buf; | ||
| 34 | - } | ||
| 35 | -} | ||
| 36 | - | ||
| 37 | -Buffer::Members::Members(std::string&& content) : | ||
| 38 | - str(std::move(content)), | ||
| 39 | - own_memory(false), | ||
| 40 | - size(str.size()), | ||
| 41 | - buf(reinterpret_cast<unsigned char*>(str.data())) | ||
| 42 | -{ | ||
| 43 | -} | ||
| 44 | - | ||
| 45 | -Buffer::Members::~Members() | ||
| 46 | -{ | ||
| 47 | - if (this->own_memory) { | ||
| 48 | - delete[] this->buf; | ||
| 49 | - } | ||
| 50 | -} | ||
| 51 | - | ||
| 52 | Buffer::Buffer() : | 32 | Buffer::Buffer() : |
| 53 | - m(new Members(0, nullptr, true)) | 33 | + m(std::make_unique<Members>()) |
| 54 | { | 34 | { |
| 55 | } | 35 | } |
| 56 | 36 | ||
| 57 | Buffer::Buffer(size_t size) : | 37 | Buffer::Buffer(size_t size) : |
| 58 | - m(new Members(size, nullptr, true)) | 38 | + m(std::make_unique<Members>(std::string(size, '\0'))) |
| 59 | { | 39 | { |
| 60 | } | 40 | } |
| 61 | 41 | ||
| 62 | Buffer::Buffer(std::string&& content) : | 42 | Buffer::Buffer(std::string&& content) : |
| 63 | - m(new Members(std::move(content))) | 43 | + m(std::make_unique<Members>(std::move(content))) |
| 64 | { | 44 | { |
| 65 | } | 45 | } |
| 66 | 46 | ||
| 67 | Buffer::Buffer(unsigned char* buf, size_t size) : | 47 | Buffer::Buffer(unsigned char* buf, size_t size) : |
| 68 | - m(new Members(size, buf, false)) | 48 | + m(std::make_unique<Members>(size, reinterpret_cast<char*>(buf))) |
| 69 | { | 49 | { |
| 70 | } | 50 | } |
| 71 | 51 | ||
| 72 | Buffer::Buffer(std::string& content) : | 52 | Buffer::Buffer(std::string& content) : |
| 73 | - m(new Members(content.size(), reinterpret_cast<unsigned char*>(content.data()), false)) | 53 | + m(std::make_unique<Members>(content.size(), content.data())) |
| 74 | { | 54 | { |
| 75 | } | 55 | } |
| 76 | 56 | ||
| @@ -88,17 +68,6 @@ Buffer::operator=(Buffer&& rhs) noexcept | @@ -88,17 +68,6 @@ Buffer::operator=(Buffer&& rhs) noexcept | ||
| 88 | 68 | ||
| 89 | Buffer::~Buffer() = default; | 69 | Buffer::~Buffer() = default; |
| 90 | 70 | ||
| 91 | -void | ||
| 92 | -Buffer::copy(Buffer const& rhs) | ||
| 93 | -{ | ||
| 94 | - if (this != &rhs) { | ||
| 95 | - m = std::unique_ptr<Members>(new Members(rhs.m->size, nullptr, true)); | ||
| 96 | - if (m->size) { | ||
| 97 | - memcpy(m->buf, rhs.m->buf, m->size); | ||
| 98 | - } | ||
| 99 | - } | ||
| 100 | -} | ||
| 101 | - | ||
| 102 | size_t | 71 | size_t |
| 103 | Buffer::getSize() const | 72 | Buffer::getSize() const |
| 104 | { | 73 | { |
| @@ -108,21 +77,67 @@ Buffer::getSize() const | @@ -108,21 +77,67 @@ Buffer::getSize() const | ||
| 108 | unsigned char const* | 77 | unsigned char const* |
| 109 | Buffer::getBuffer() const | 78 | Buffer::getBuffer() const |
| 110 | { | 79 | { |
| 111 | - return m->buf; | 80 | + return reinterpret_cast<unsigned char*>(m->buf); |
| 112 | } | 81 | } |
| 113 | 82 | ||
| 114 | unsigned char* | 83 | unsigned char* |
| 115 | Buffer::getBuffer() | 84 | Buffer::getBuffer() |
| 116 | { | 85 | { |
| 117 | - return m->buf; | 86 | + return reinterpret_cast<unsigned char*>(m->buf); |
| 118 | } | 87 | } |
| 119 | 88 | ||
| 120 | Buffer | 89 | Buffer |
| 121 | Buffer::copy() const | 90 | Buffer::copy() const |
| 122 | { | 91 | { |
| 123 | - auto result = Buffer(m->size); | ||
| 124 | - if (m->size) { | ||
| 125 | - memcpy(result.m->buf, m->buf, m->size); | 92 | + if (m->size == 0) { |
| 93 | + return {}; | ||
| 94 | + } | ||
| 95 | + return {std::string(m->buf, m->size)}; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +std::string | ||
| 99 | +Buffer::move() | ||
| 100 | +{ | ||
| 101 | + if (m->size == 0) { | ||
| 102 | + return {}; | ||
| 126 | } | 103 | } |
| 127 | - return result; | 104 | + if (!m->str.empty()) { |
| 105 | + m->size = 0; | ||
| 106 | + m->buf = nullptr; | ||
| 107 | + return std::move(m->str); | ||
| 108 | + } | ||
| 109 | + return {m->buf, m->size}; | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +std::string_view | ||
| 113 | +Buffer::view() const | ||
| 114 | +{ | ||
| 115 | + if (!m->buf) { | ||
| 116 | + return {}; | ||
| 117 | + } | ||
| 118 | + return {m->buf, m->size}; | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +char const* | ||
| 122 | +Buffer::data() const | ||
| 123 | +{ | ||
| 124 | + return m->buf ? m->buf : m->str.data(); | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +char* | ||
| 128 | +Buffer::data() | ||
| 129 | +{ | ||
| 130 | + return m->buf ? m->buf : m->str.data(); | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +bool | ||
| 134 | +Buffer::empty() const | ||
| 135 | +{ | ||
| 136 | + return m->size == 0; | ||
| 137 | +} | ||
| 138 | + | ||
| 139 | +size_t | ||
| 140 | +Buffer::size() const | ||
| 141 | +{ | ||
| 142 | + return m->size; | ||
| 128 | } | 143 | } |
libtests/buffer.cc
| @@ -35,6 +35,37 @@ main() | @@ -35,6 +35,37 @@ main() | ||
| 35 | assert(bc2p[0] == 'R'); | 35 | assert(bc2p[0] == 'R'); |
| 36 | assert(bc2p[1] == 'W'); | 36 | assert(bc2p[1] == 'W'); |
| 37 | 37 | ||
| 38 | + // Test move, view, data, empty and size methods | ||
| 39 | + assert(bc1.view() == "RW"); | ||
| 40 | + assert(std::string_view(bc1.data(), bc1.getSize()) == "RW"); | ||
| 41 | + *bc1.data() = 'Z'; | ||
| 42 | + assert(!bc1.empty()); | ||
| 43 | + assert(bc1.size() == 2); | ||
| 44 | + auto s1 = bc1.move(); | ||
| 45 | + assert(bc1.empty()); | ||
| 46 | + assert(bc1.size() == 0); | ||
| 47 | + assert(!bc1.getBuffer()); | ||
| 48 | + assert(bc1.getSize() == 0); | ||
| 49 | + assert(s1 == "ZW"); | ||
| 50 | + assert(bc1.view().empty()); | ||
| 51 | + assert(bc1.data()); | ||
| 52 | + assert(Buffer(s1).move() == "ZW"); | ||
| 53 | + assert(s1 == "ZW"); | ||
| 54 | + | ||
| 55 | + // Test const methods | ||
| 56 | + const Buffer cb(s1); | ||
| 57 | + assert(*cb.data() == 'Z'); | ||
| 58 | + assert(*(cb.getBuffer() + 1) == 'W'); | ||
| 59 | + | ||
| 60 | + // Test constructors | ||
| 61 | + assert(Buffer().empty()); | ||
| 62 | + assert(Buffer().copy().empty()); | ||
| 63 | + assert(!Buffer().getBuffer()); | ||
| 64 | + assert(Buffer().data()); | ||
| 65 | + assert(Buffer().move().empty()); | ||
| 66 | + assert(Buffer(s1).size() == 2); | ||
| 67 | + assert(*Buffer(s1).data() == 'Z'); | ||
| 68 | + | ||
| 38 | // Test Buffer(std:string&&) | 69 | // Test Buffer(std:string&&) |
| 39 | Buffer bc3("QW"); | 70 | Buffer bc3("QW"); |
| 40 | unsigned char* bc3p = bc3.getBuffer(); | 71 | unsigned char* bc3p = bc3.getBuffer(); |
manual/release-notes.rst
| @@ -38,9 +38,15 @@ more detail. | @@ -38,9 +38,15 @@ more detail. | ||
| 38 | overhead of repeatedly validating the underlying document structure | 38 | overhead of repeatedly validating the underlying document structure |
| 39 | and/or building internal caches. If the underlying document structure | 39 | and/or building internal caches. If the underlying document structure |
| 40 | is directly modified (without the use of DocumentHelpers), the | 40 | is directly modified (without the use of DocumentHelpers), the |
| 41 | - ``validate`` methods revalidates the structure and resynchronizes any | 41 | + ``validate`` methods revalidate the structure and resynchronize any |
| 42 | internal caches. | 42 | internal caches. |
| 43 | 43 | ||
| 44 | + - Add new ``Buffer`` methods ``move``, ``view``, ``data``, ``size`` and | ||
| 45 | + ``empty``. The new methods present the ``Buffer`` as a ``char`` (rather | ||
| 46 | + than ``unsigned char``) container and facilitate the efficient moving | ||
| 47 | + of its content into a `std::string``. | ||
| 48 | + | ||
| 49 | + | ||
| 44 | - CLI Enhancements | 50 | - CLI Enhancements |
| 45 | 51 | ||
| 46 | - Disallow option :qpdf:ref:`--deterministic-id` to be used together | 52 | - Disallow option :qpdf:ref:`--deterministic-id` to be used together |