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 | 25 | #include <cstddef> |
| 26 | 26 | #include <memory> |
| 27 | 27 | #include <string> |
| 28 | +#include <string_view> | |
| 28 | 29 | |
| 29 | 30 | class Buffer |
| 30 | 31 | { |
| ... | ... | @@ -66,11 +67,34 @@ class Buffer |
| 66 | 67 | QPDF_DLL |
| 67 | 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 | 95 | private: |
| 70 | 96 | class Members; |
| 71 | 97 | |
| 72 | - void copy(Buffer const&); | |
| 73 | - | |
| 74 | 98 | std::unique_ptr<Members> m; |
| 75 | 99 | }; |
| 76 | 100 | ... | ... |
libqpdf/Buffer.cc
| ... | ... | @@ -2,75 +2,55 @@ |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/Buffer.hh> |
| 4 | 4 | |
| 5 | -#include <cstring> | |
| 6 | - | |
| 7 | 5 | class Buffer::Members |
| 8 | 6 | { |
| 9 | 7 | friend class Buffer; |
| 10 | 8 | |
| 11 | 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 | 23 | Members(Members const&) = delete; |
| 24 | + ~Members() = default; | |
| 18 | 25 | |
| 26 | + private: | |
| 19 | 27 | std::string str; |
| 20 | - bool own_memory; | |
| 21 | 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 | 32 | Buffer::Buffer() : |
| 53 | - m(new Members(0, nullptr, true)) | |
| 33 | + m(std::make_unique<Members>()) | |
| 54 | 34 | { |
| 55 | 35 | } |
| 56 | 36 | |
| 57 | 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 | 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 | 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 | 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 | 68 | |
| 89 | 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 | 71 | size_t |
| 103 | 72 | Buffer::getSize() const |
| 104 | 73 | { |
| ... | ... | @@ -108,21 +77,67 @@ Buffer::getSize() const |
| 108 | 77 | unsigned char const* |
| 109 | 78 | Buffer::getBuffer() const |
| 110 | 79 | { |
| 111 | - return m->buf; | |
| 80 | + return reinterpret_cast<unsigned char*>(m->buf); | |
| 112 | 81 | } |
| 113 | 82 | |
| 114 | 83 | unsigned char* |
| 115 | 84 | Buffer::getBuffer() |
| 116 | 85 | { |
| 117 | - return m->buf; | |
| 86 | + return reinterpret_cast<unsigned char*>(m->buf); | |
| 118 | 87 | } |
| 119 | 88 | |
| 120 | 89 | Buffer |
| 121 | 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 | 35 | assert(bc2p[0] == 'R'); |
| 36 | 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 | 69 | // Test Buffer(std:string&&) |
| 39 | 70 | Buffer bc3("QW"); |
| 40 | 71 | unsigned char* bc3p = bc3.getBuffer(); | ... | ... |
manual/release-notes.rst
| ... | ... | @@ -38,9 +38,15 @@ more detail. |
| 38 | 38 | overhead of repeatedly validating the underlying document structure |
| 39 | 39 | and/or building internal caches. If the underlying document structure |
| 40 | 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 | 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 | 50 | - CLI Enhancements |
| 45 | 51 | |
| 46 | 52 | - Disallow option :qpdf:ref:`--deterministic-id` to be used together | ... | ... |