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 PerconaFT.
6
7
8Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT 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 PerconaFT 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 PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35======= */
36
37#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39#include <string.h>
40
41#include "portability/memory.h"
42
43#include "locktree/range_buffer.h"
44#include "util/dbt.h"
45
46namespace toku {
47
48 bool range_buffer::record_header::left_is_infinite(void) const {
49 return left_neg_inf || left_pos_inf;
50 }
51
52 bool range_buffer::record_header::right_is_infinite(void) const {
53 return right_neg_inf || right_pos_inf;
54 }
55
56 void range_buffer::record_header::init(const DBT *left_key, const DBT *right_key) {
57 left_neg_inf = left_key == toku_dbt_negative_infinity();
58 left_pos_inf = left_key == toku_dbt_positive_infinity();
59 left_key_size = toku_dbt_is_infinite(left_key) ? 0 : left_key->size;
60 if (right_key) {
61 right_neg_inf = right_key == toku_dbt_negative_infinity();
62 right_pos_inf = right_key == toku_dbt_positive_infinity();
63 right_key_size = toku_dbt_is_infinite(right_key) ? 0 : right_key->size;
64 } else {
65 right_neg_inf = left_neg_inf;
66 right_pos_inf = left_pos_inf;
67 right_key_size = 0;
68 }
69 }
70
71 const DBT *range_buffer::iterator::record::get_left_key(void) const {
72 if (_header.left_neg_inf) {
73 return toku_dbt_negative_infinity();
74 } else if (_header.left_pos_inf) {
75 return toku_dbt_positive_infinity();
76 } else {
77 return &_left_key;
78 }
79 }
80
81 const DBT *range_buffer::iterator::record::get_right_key(void) const {
82 if (_header.right_neg_inf) {
83 return toku_dbt_negative_infinity();
84 } else if (_header.right_pos_inf) {
85 return toku_dbt_positive_infinity();
86 } else {
87 return &_right_key;
88 }
89 }
90
91 size_t range_buffer::iterator::record::size(void) const {
92 return sizeof(record_header) + _header.left_key_size + _header.right_key_size;
93 }
94
95 void range_buffer::iterator::record::deserialize(const char *buf) {
96 size_t current = 0;
97
98 // deserialize the header
99 memcpy(&_header, buf, sizeof(record_header));
100 current += sizeof(record_header);
101
102 // deserialize the left key if necessary
103 if (!_header.left_is_infinite()) {
104 // point the left DBT's buffer into ours
105 toku_fill_dbt(&_left_key, buf + current, _header.left_key_size);
106 current += _header.left_key_size;
107 }
108
109 // deserialize the right key if necessary
110 if (!_header.right_is_infinite()) {
111 if (_header.right_key_size == 0) {
112 toku_copyref_dbt(&_right_key, _left_key);
113 } else {
114 toku_fill_dbt(&_right_key, buf + current, _header.right_key_size);
115 }
116 }
117 }
118
119 toku::range_buffer::iterator::iterator() :
120 _ma_chunk_iterator(nullptr),
121 _current_chunk_base(nullptr),
122 _current_chunk_offset(0), _current_chunk_max(0),
123 _current_rec_size(0) {
124 }
125
126 toku::range_buffer::iterator::iterator(const range_buffer *buffer) :
127 _ma_chunk_iterator(&buffer->_arena),
128 _current_chunk_base(nullptr),
129 _current_chunk_offset(0), _current_chunk_max(0),
130 _current_rec_size(0) {
131 reset_current_chunk();
132 }
133
134 void range_buffer::iterator::reset_current_chunk() {
135 _current_chunk_base = _ma_chunk_iterator.current(&_current_chunk_max);
136 _current_chunk_offset = 0;
137 }
138
139 bool range_buffer::iterator::current(record *rec) {
140 if (_current_chunk_offset < _current_chunk_max) {
141 const char *buf = reinterpret_cast<const char *>(_current_chunk_base);
142 rec->deserialize(buf + _current_chunk_offset);
143 _current_rec_size = rec->size();
144 return true;
145 } else {
146 return false;
147 }
148 }
149
150 // move the iterator to the next record in the buffer
151 void range_buffer::iterator::next(void) {
152 invariant(_current_chunk_offset < _current_chunk_max);
153 invariant(_current_rec_size > 0);
154
155 // the next record is _current_rec_size bytes forward
156 _current_chunk_offset += _current_rec_size;
157 // now, we don't know how big the current is, set it to 0.
158 _current_rec_size = 0;
159
160 if (_current_chunk_offset >= _current_chunk_max) {
161 // current chunk is exhausted, try moving to the next one
162 if (_ma_chunk_iterator.more()) {
163 _ma_chunk_iterator.next();
164 reset_current_chunk();
165 }
166 }
167 }
168
169 void range_buffer::create(void) {
170 // allocate buffer space lazily instead of on creation. this way,
171 // no malloc/free is done if the transaction ends up taking no locks.
172 _arena.create(0);
173 _num_ranges = 0;
174 }
175
176 void range_buffer::append(const DBT *left_key, const DBT *right_key) {
177 // if the keys are equal, then only one copy is stored.
178 if (toku_dbt_equals(left_key, right_key)) {
179 invariant(left_key->size <= MAX_KEY_SIZE);
180 append_point(left_key);
181 } else {
182 invariant(left_key->size <= MAX_KEY_SIZE);
183 invariant(right_key->size <= MAX_KEY_SIZE);
184 append_range(left_key, right_key);
185 }
186 _num_ranges++;
187 }
188
189 bool range_buffer::is_empty(void) const {
190 return total_memory_size() == 0;
191 }
192
193 uint64_t range_buffer::total_memory_size(void) const {
194 return _arena.total_size_in_use();
195 }
196
197 int range_buffer::get_num_ranges(void) const {
198 return _num_ranges;
199 }
200
201 void range_buffer::destroy(void) {
202 _arena.destroy();
203 }
204
205 void range_buffer::append_range(const DBT *left_key, const DBT *right_key) {
206 size_t record_length = sizeof(record_header) + left_key->size + right_key->size;
207 char *buf = reinterpret_cast<char *>(_arena.malloc_from_arena(record_length));
208
209 record_header h;
210 h.init(left_key, right_key);
211
212 // serialize the header
213 memcpy(buf, &h, sizeof(record_header));
214 buf += sizeof(record_header);
215
216 // serialize the left key if necessary
217 if (!h.left_is_infinite()) {
218 memcpy(buf, left_key->data, left_key->size);
219 buf += left_key->size;
220 }
221
222 // serialize the right key if necessary
223 if (!h.right_is_infinite()) {
224 memcpy(buf, right_key->data, right_key->size);
225 }
226 }
227
228 void range_buffer::append_point(const DBT *key) {
229 size_t record_length = sizeof(record_header) + key->size;
230 char *buf = reinterpret_cast<char *>(_arena.malloc_from_arena(record_length));
231
232 record_header h;
233 h.init(key, nullptr);
234
235 // serialize the header
236 memcpy(buf, &h, sizeof(record_header));
237 buf += sizeof(record_header);
238
239 // serialize the key if necessary
240 if (!h.left_is_infinite()) {
241 memcpy(buf, key->data, key->size);
242 }
243 }
244
245} /* namespace toku */
246