1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: |
3 | #ident "$Id$" |
4 | /*====== |
5 | This file is part of TokuDB |
6 | |
7 | |
8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
9 | |
10 | TokuDBis is free software: you can redistribute it and/or modify |
11 | it under the terms of the GNU General Public License, version 2, |
12 | as published by the Free Software Foundation. |
13 | |
14 | TokuDB is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with TokuDB. If not, see <http://www.gnu.org/licenses/>. |
21 | |
22 | ======= */ |
23 | |
24 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
25 | |
26 | #ifndef _TOKUDB_BUFFER_H |
27 | #define _TOKUDB_BUFFER_H |
28 | |
29 | #include "hatoku_defines.h" |
30 | #include "tokudb_debug.h" |
31 | #include "tokudb_thread.h" |
32 | #include "tokudb_vlq.h" |
33 | |
34 | namespace tokudb { |
35 | |
36 | // A Buffer manages a contiguous chunk of memory and supports appending new |
37 | // data to the end of the buffer, and consuming chunks from the beginning of |
38 | // the buffer. The buffer will reallocate memory when appending new data to |
39 | // a full buffer. |
40 | |
41 | class buffer { |
42 | public: |
43 | inline buffer( |
44 | void* the_data, |
45 | size_t s, |
46 | size_t l) : |
47 | m_data(the_data), |
48 | m_size(s), |
49 | m_limit(l), |
50 | m_is_static(true) { |
51 | } |
52 | inline buffer() : |
53 | m_data(NULL), |
54 | m_size(0), |
55 | m_limit(0), |
56 | m_is_static(false) { |
57 | } |
58 | virtual ~buffer() { |
59 | if (!m_is_static) |
60 | free(m_data); |
61 | } |
62 | |
63 | // Return a pointer to the end of the buffer suitable for appending a |
64 | // fixed number of bytes. |
65 | void* append_ptr(size_t s) { |
66 | maybe_realloc(s); |
67 | void* p = (char*)m_data + m_size; |
68 | m_size += s; |
69 | return p; |
70 | } |
71 | |
72 | // Append bytes to the buffer |
73 | void append(void* p, size_t s) { |
74 | memcpy(append_ptr(s), p, s); |
75 | } |
76 | |
77 | // Append an unsigned int to the buffer. |
78 | // Returns the number of bytes used to encode the number. |
79 | // Returns 0 if the number could not be encoded. |
80 | template<class T> size_t append_ui(T n) { |
81 | maybe_realloc(10); // 10 bytes is big enough for up to 64 bit number |
82 | size_t s = tokudb::vlq_encode_ui<T>(n, (char *) m_data + m_size, 10); |
83 | m_size += s; |
84 | return s; |
85 | } |
86 | |
87 | // Return a pointer to the next location in the buffer where bytes are |
88 | // consumed from. |
89 | void* consume_ptr(size_t s) { |
90 | if (m_size + s > m_limit) |
91 | return NULL; |
92 | void* p = (char*)m_data + m_size; |
93 | m_size += s; |
94 | return p; |
95 | } |
96 | |
97 | // Consume bytes from the buffer. |
98 | void consume(void* p, size_t s) { |
99 | memcpy(p, consume_ptr(s), s); |
100 | } |
101 | |
102 | // Consume an unsigned int from the buffer. |
103 | // Returns 0 if the unsigned int could not be decoded, probably because |
104 | // the buffer is too short. |
105 | // Otherwise return the number of bytes consumed, and stuffs the decoded |
106 | // number in *p. |
107 | template<class T> size_t consume_ui(T* p) { |
108 | size_t s = tokudb::vlq_decode_ui<T>( |
109 | p, |
110 | (char*)m_data + m_size, |
111 | m_limit - m_size); |
112 | m_size += s; |
113 | return s; |
114 | } |
115 | |
116 | // Write p_length bytes at an offset in the buffer |
117 | void write(void* p, size_t p_length, size_t offset) { |
118 | assert_always(offset + p_length <= m_size); |
119 | memcpy((char*)m_data + offset, p, p_length); |
120 | } |
121 | |
122 | // Read p_length bytes at an offset in the buffer |
123 | void read(void* p, size_t p_length, size_t offset) { |
124 | assert_always(offset + p_length <= m_size); |
125 | memcpy(p, (char*)m_data + offset, p_length); |
126 | } |
127 | |
128 | // Replace a field in the buffer with new data. If the new data size is |
129 | // different, then readjust the size of the buffer and move things around. |
130 | void replace(size_t offset, size_t old_s, void* new_p, size_t new_s) { |
131 | assert_always(offset + old_s <= m_size); |
132 | if (new_s > old_s) |
133 | maybe_realloc(new_s - old_s); |
134 | char* data_offset = (char*)m_data + offset; |
135 | if (new_s != old_s) { |
136 | size_t n = m_size - (offset + old_s); |
137 | assert_always( |
138 | offset + new_s + n <= m_limit && offset + old_s + n <= m_limit); |
139 | memmove(data_offset + new_s, data_offset + old_s, n); |
140 | if (new_s > old_s) |
141 | m_size += new_s - old_s; |
142 | else |
143 | m_size -= old_s - new_s; |
144 | assert_always(m_size <= m_limit); |
145 | } |
146 | memcpy(data_offset, new_p, new_s); |
147 | } |
148 | |
149 | // Return a pointer to the data in the buffer |
150 | void* data() const { |
151 | return m_data; |
152 | } |
153 | |
154 | // Return the size of the data in the buffer |
155 | size_t size() const { |
156 | return m_size; |
157 | } |
158 | |
159 | // Return the size of the underlying memory in the buffer |
160 | size_t limit() const { |
161 | return m_limit; |
162 | } |
163 | |
164 | private: |
165 | // Maybe reallocate the buffer when it becomes full by doubling its size. |
166 | void maybe_realloc(size_t s) { |
167 | if (m_size + s > m_limit) { |
168 | size_t new_limit = m_limit * 2; |
169 | if (new_limit < m_size + s) |
170 | new_limit = m_size + s; |
171 | assert_always(!m_is_static); |
172 | void *new_data = realloc(m_data, new_limit); |
173 | assert_always(new_data != NULL); |
174 | m_data = new_data; |
175 | m_limit = new_limit; |
176 | } |
177 | } |
178 | private: |
179 | void* m_data; |
180 | size_t m_size; |
181 | size_t m_limit; |
182 | bool m_is_static; |
183 | }; |
184 | |
185 | }; |
186 | |
187 | #endif |
188 | |