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/*======
5This file is part of TokuDB
6
7
8Copyright (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
34namespace 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
41class buffer {
42public:
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
164private:
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 }
178private:
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