2 * Copyright (C) 2017 Cisco Inc.
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,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // @author Changxue Deng <chadeng@cisco.com>
21 #include "integer_4b_5b.h"
26 typedef struct _iterator_node
31 uint16_t bucket_index;
34 static void free_iterator_node(void *n)
39 iterator_node *inode = (iterator_node *) n;
40 if(inode->key != NULL)
42 if(inode->data != NULL)
48 static iterator_node* new_iterator_node(const std::string &key, MBData *mbdata)
50 iterator_node *inode = (iterator_node *) malloc(sizeof(*inode));
52 throw (int) MBError::NO_MEMORY;
54 inode->key = new std::string(key);
57 inode->bucket_index = mbdata->bucket_index;
58 mbdata->TransferValueTo(inode->data, inode->data_len);
59 if(inode->data == NULL || inode->data_len <= 0)
61 free_iterator_node(inode);
74 /////////////////////////////////////////////////////////////////////
76 // Example to use DB iterator
77 // for(DB::iterator iter = db.begin(); iter != db.end(); ++iter) {
78 // std::cout << iter.key << "\n";
80 /////////////////////////////////////////////////////////////////////
82 const DB::iterator DB::begin(bool check_async_mode, bool rc_mode) const
84 DB::iterator iter = iterator(*this, DB_ITER_STATE_INIT);
85 if(rc_mode) iter.value.options |= CONSTS::OPTION_RC_MODE;
86 iter.init(check_async_mode);
91 const DB::iterator DB::end() const
93 return iterator(*this, DB_ITER_STATE_DONE);
96 void DB::iterator::iter_obj_init()
102 if(!(db_ref.GetDBOptions() & CONSTS::ACCESS_MODE_WRITER))
105 lfree = db_ref.dict->GetLockFreePtr();
109 if(state == DB_ITER_STATE_INIT)
110 state = DB_ITER_STATE_MORE;
113 DB::iterator::iterator(const DB &db, int iter_state)
114 : db_ref(db), state(iter_state)
119 DB::iterator::iterator(const iterator &rhs)
120 : db_ref(rhs.db_ref), state(rhs.state)
125 DB::iterator::~iterator()
127 if(node_stack != NULL)
129 if(kv_per_node != NULL)
133 // Initialize the iterator, get the very first key-value pair.
134 void DB::iterator::init(bool check_async_mode)
136 // Writer in async mode cannot be used for lookup
137 if(check_async_mode && (db_ref.options & CONSTS::ASYNC_WRITER_MODE))
139 state = DB_ITER_STATE_DONE;
143 node_stack = new MBlsq(free_iterator_node);
144 kv_per_node = new MBlsq(free_iterator_node);
146 load_kv_for_node("");
148 state = DB_ITER_STATE_DONE;
151 // Initialize the iterator, but do not get the first key-value pair.
152 // This is used for resource collection.
153 int DB::iterator::init_no_next()
155 node_stack = new MBlsq(NULL);
158 int rval = db_ref.dict->ReadRootNode(node_buff, edge_ptrs, match, value);
159 if(rval != MBError::SUCCESS)
160 state = DB_ITER_STATE_DONE;
164 const DB::iterator& DB::iterator::operator++()
167 state = DB_ITER_STATE_DONE;
172 // This overloaded operator should only be used for iterator state check.
173 bool DB::iterator::operator!=(const iterator &rhs)
175 return state != rhs.state;
178 int DB::iterator::get_node_offset(const std::string &node_key,
179 size_t &parent_edge_off,
185 value.options |= CONSTS::OPTION_FIND_AND_STORE_PARENT;
188 rval = db_ref.dict->Find((const uint8_t *)node_key.data(),
189 node_key.size(), value);
190 if(rval != MBError::TRY_AGAIN)
192 nanosleep((const struct timespec[]){{0, 10L}}, NULL);
195 if(rval == MBError::IN_DICT)
197 parent_edge_off = edge_ptrs.parent_offset;
198 node_offset = Get6BInteger(value.edge_ptrs.offset_ptr);
199 rval = MBError::SUCCESS;
204 int DB::iterator::load_kvs(const std::string &curr_node_key,
205 MBlsq *child_node_list)
208 size_t child_node_off;
209 std::string match_str;
210 iterator_node *inode;
216 rval = db_ref.dict->ReadNextEdge(node_buff, edge_ptrs, match, value,
217 match_str, child_node_off);
222 LockFreeData snapshot;
224 size_t edge_off_prev = edge_ptrs.offset;
225 lfree->ReaderLockFreeStart(snapshot);
227 rval = db_ref.dict->ReadNextEdge(node_buff, edge_ptrs, match, value,
228 match_str, child_node_off);
230 lf_ret = lfree->ReaderLockFreeStop(snapshot, edge_off_prev, value);
231 if(lf_ret == MBError::TRY_AGAIN)
236 if(rval != MBError::SUCCESS)
239 match_str = curr_node_key + match_str;
240 if(child_node_off > 0)
242 inode = new_iterator_node(match_str, NULL);
245 rval = child_node_list->AddToTail(inode);
246 if(rval != MBError::SUCCESS)
248 free_iterator_node(inode);
254 if(match != MATCH_NONE)
256 inode = new_iterator_node(match_str, &value);
259 rval = kv_per_node->AddToTail(inode);
260 if(rval != MBError::SUCCESS)
262 free_iterator_node(inode);
269 if(rval == MBError::OUT_OF_BOUND)
270 rval = MBError::SUCCESS;
274 int DB::iterator::load_node(const std::string &curr_node_key,
275 size_t &parent_edge_off)
279 if(curr_node_key.size() == 0)
281 rval = db_ref.dict->ReadRootNode(node_buff, edge_ptrs, match, value);
286 rval = get_node_offset(curr_node_key, parent_edge_off, node_offset);
287 if(rval != MBError::SUCCESS)
289 rval = db_ref.dict->ReadNode(node_offset, node_buff, edge_ptrs, match,
296 int DB::iterator::load_kv_for_node(const std::string &curr_node_key)
299 MBlsq child_node_list(free_iterator_node);
300 size_t parent_edge_off;
304 rval = load_node(curr_node_key, parent_edge_off);
305 if(rval == MBError::SUCCESS)
306 rval = load_kvs(curr_node_key, &child_node_list);
311 LockFreeData snapshot;
317 lfree->ReaderLockFreeStart(snapshot);
319 rval = load_node(curr_node_key, parent_edge_off);
320 if(rval == MBError::SUCCESS)
322 rval = load_kvs(curr_node_key, &child_node_list);
324 if(rval == MBError::TRY_AGAIN)
326 kv_per_node->Clear();
327 child_node_list.Clear();
333 lf_ret = lfree->ReaderLockFreeStop(snapshot, parent_edge_off, value);
334 if(lf_ret == MBError::TRY_AGAIN)
336 kv_per_node->Clear();
337 child_node_list.Clear();
345 if(rval == MBError::SUCCESS)
347 iterator_node *inode;
348 while((inode = (iterator_node *) child_node_list.RemoveFromHead()))
350 node_stack->AddToHead(inode);
355 std::cerr << "failed to run ietrator: " << MBError::get_error_str(rval) << "\n";
356 kv_per_node->Clear();
357 child_node_list.Clear();
362 // Find next iterator match
363 DB::iterator* DB::iterator::next()
365 iterator_node *inode;
367 while(kv_per_node->Count() == 0)
369 inode = (iterator_node *) node_stack->RemoveFromHead();
373 int rval = load_kv_for_node(*inode->key);
374 free_iterator_node(inode);
375 if(rval != MBError::SUCCESS)
379 if(kv_per_node->Count() > 0)
381 inode = (iterator_node *) kv_per_node->RemoveFromHead();
382 match = MATCH_NODE_OR_EDGE;
384 value.TransferValueFrom(inode->data, inode->data_len);
385 value.bucket_index = inode->bucket_index;
386 free_iterator_node(inode);
393 // There is no need to perform lock-free check in next_dbt_buffer
394 // since it can only be called by writer.
395 bool DB::iterator::next_dbt_buffer(struct _DBTraverseNode *dbt_n)
399 size_t curr_edge_off;
400 std::string match_str;
402 memset(dbt_n, 0, sizeof(*dbt_n));
404 curr_edge_off = edge_ptrs.offset;
405 while((rval = db_ref.dict->ReadNextEdge(node_buff, edge_ptrs, match,
406 value, match_str, node_off, false)) == MBError::SUCCESS)
408 if(edge_ptrs.len_ptr[0] > LOCAL_EDGE_LEN)
410 dbt_n->edgestr_offset = Get5BInteger(edge_ptrs.ptr);
411 dbt_n->edgestr_size = edge_ptrs.len_ptr[0] - 1;
412 dbt_n->edgestr_link_offset = curr_edge_off;
413 dbt_n->buffer_type |= BUFFER_TYPE_EDGE_STR;
418 dbt_n->node_offset = node_off;
419 dbt_n->node_link_offset = curr_edge_off + EDGE_NODE_LEADING_POS;
420 dbt_n->buffer_type |= BUFFER_TYPE_NODE;
421 db_ref.dict->ReadNodeHeader(node_off, dbt_n->node_size, match, dbt_n->data_offset,
422 dbt_n->data_link_offset);
423 if(match == MATCH_NODE)
424 dbt_n->buffer_type |= BUFFER_TYPE_DATA;
426 else if(match == MATCH_EDGE)
428 dbt_n->data_offset = Get6BInteger(edge_ptrs.offset_ptr);
429 dbt_n->data_link_offset = curr_edge_off + EDGE_NODE_LEADING_POS;
430 dbt_n->buffer_type |= BUFFER_TYPE_DATA;
433 if(dbt_n->buffer_type != BUFFER_TYPE_NONE)
435 dbt_n->edge_offset = curr_edge_off;
439 curr_edge_off = edge_ptrs.offset;
442 if(rval == MBError::OUT_OF_BOUND)
444 node_off = (size_t ) node_stack->RemoveIntFromHead();
447 rval = db_ref.dict->ReadNode(node_off, node_buff, edge_ptrs, match,
449 if(rval != MBError::SUCCESS)
461 // Add an node offset to the iterator queue
462 void DB::iterator::add_node_offset(size_t node_offset)
464 int rval = node_stack->AddIntToHead(node_offset);
465 if(rval != MBError::SUCCESS)