1 /*
2  * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License, version 2.0,
6  * as published by the Free Software Foundation.
7  *
8  * This program is also distributed with certain software (including
9  * but not limited to OpenSSL) that is licensed under separate terms,
10  * as designated in a particular file or component or in included license
11  * documentation.  The authors of MySQL hereby grant you an additional
12  * permission to link the program and your derivative works with the
13  * separately licensed software that they have included with MySQL.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License, version 2.0, for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301  USA
24  */
25 
26 #include "ngs/protocol/output_buffer.h"
27 
28 #include "my_byteorder.h"
29 
30 
31 namespace ngs {
32 
Output_buffer(Page_pool & page_pool)33 Output_buffer::Output_buffer(Page_pool& page_pool)
34   : Buffer(page_pool) {
35 }
36 
37 
add_int32(int32_t i)38 bool Output_buffer::add_int32(int32_t i) {
39   const uint32 raw_data_size = sizeof(int32_t);
40   uchar raw_data[raw_data_size];
41   int4store(raw_data, i);
42 
43   add_bytes(reinterpret_cast<char*>(raw_data),
44             raw_data_size);
45 
46   return true;
47 }
48 
add_int8(int8_t i)49 bool Output_buffer::add_int8(int8_t i) {
50   void *ptr;
51   int size;
52 
53   do {
54     if (!Next(&ptr, &size))
55       return false;
56   }
57   while (size < 1);
58 
59   *(int8_t*)ptr = i;
60 
61   if (size > 1) // return leftover
62     BackUp(size-1);
63 
64   return true;
65 }
66 
67 
add_bytes(const char * data,size_t length)68 bool Output_buffer::add_bytes(const char *data, size_t length) {
69   void *ptr;
70   int size;
71 
72   do {
73     if (!Next(&ptr, &size) || size < 0)
74       return false;
75 
76     if ((size_t)size >= length) {
77       memcpy(ptr, data, length);
78       BackUp(static_cast<int>(size - length));
79       length = 0;
80     }
81     else {
82       memcpy(ptr, data, size);
83       data += size;
84       length -= size;
85     }
86   }
87   while (length > 0);
88 
89   return true;
90 }
91 
92 
Next(void ** data,int * size)93 bool Output_buffer::Next(void** data, int* size) {
94   // point *data to the beginning of the next page
95   // point size to the data left in that page
96   // only up to m_artificial_length must be passed
97 
98   // first, check if there are pages left with free space
99   for (Page_list::const_iterator p = m_pages.begin();
100        p != m_pages.end();
101        ++p) {
102     if ((*p)->length < (*p)->capacity) {
103       // ensure that the next page is empty
104       Page_list::const_iterator next = p;
105       ++next;
106       if (next == m_pages.end() || (*next)->length == 0) {
107         *data = (*p)->data + (*p)->length;
108         *size = (*p)->capacity - (*p)->length;
109         (*p)->length = (*p)->capacity;
110         m_length += *size;
111         return true;
112       }
113     }
114   }
115 
116   // no more space left, just add new pages
117   if (Memory_allocated == add_pages(1)) {
118     Buffer_page &p = m_pages.back();
119 
120     *data = p->data;
121     *size = p->capacity;
122     p->length = p->capacity;
123     m_length += *size;
124     return true;
125   }
126   return false;
127 }
128 
129 
BackUp(int count)130 void Output_buffer::BackUp(int count) {
131   // return unused bytes from the last Next() call
132   for (Page_list::const_reverse_iterator p = m_pages.rbegin();
133        p != m_pages.rend() && count > 0; ++p)
134   {
135     if ((*p)->length > 0)
136     {
137       if (count > 0 && (size_t)count < (*p)->length)
138       {
139         (*p)->length -= count;
140         m_length -= count;
141         count = 0;
142       }
143       else
144       {
145         count -= (*p)->length;
146         m_length -= (*p)->length;
147         (*p)->length = 0;
148       }
149     }
150   }
151 }
152 
153 
ByteCount() const154 int64_t Output_buffer::ByteCount() const {
155   size_t count = 0;
156   for (Page_list::const_iterator p = m_pages.begin();
157        p != m_pages.end(); ++p)
158     count += (*p)->length;
159   return count;
160 }
161 
162 
get_buffers()163 Const_buffer_sequence Output_buffer::get_buffers() {
164   Const_buffer_sequence buffers;
165   buffers.reserve(m_pages.size());
166 
167   for (Page_list::const_iterator p = m_pages.begin();
168        p != m_pages.end() && (*p)->length > 0;++p) {
169     buffers.push_back(std::make_pair((*p)->data, (*p)->length));
170   }
171   return buffers;
172 }
173 
save_state()174 void Output_buffer::save_state() {
175   m_saved_length = m_length;
176   Page_list::iterator it = m_pages.begin();
177 
178   for (; it != m_pages.end(); ++it) {
179     (*it)->save_state();
180   }
181 }
182 
rollback()183 void Output_buffer::rollback() {
184   m_length = m_saved_length;
185   Page_list::iterator it = m_pages.begin();
186 
187   for (; it != m_pages.end(); ++it) {
188     (*it)->rollback();
189   }
190 }
191 
192 } // namespace ngs
193