Commit afb09d2eaaed64af03021b56f2ef3923dd6fccde

Authored by m-holger
Committed by GitHub
2 parents 88a62f78 82c178b0

Merge pull request #1551 from m-holger/buffer

Refactor Buffer
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&amp;&amp; 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
... ...