mirror of
https://github.com/ceph/ceph
synced 2024-12-26 21:43:10 +00:00
elist: simpler embedded list
This is more or less equivalent to the linux kernel list_head: each embedded item struct has only a next and prev pointer. As long as the same member item is always used, at a fixed offset from the containing class, we can go from an item to a contained class. The offset can either be passed to the list (head) constructor, or to the begin(), front(), back() members explicitly. Iterator has 3 modes.. current (list_for_each), cache_next (list_for_each_safe), and magic (uses cached next iff current is empty). Magic will work most of the time... as long as we don't re-add ourselves to a different list inside the iterator loop. (Note that if we do, we will iterator up to the other list's head, not detect it is a head, an get an invalid pointer and crash.) elist: add to makefile elist: require offset for cosntructor elist: fix pop_front/back
This commit is contained in:
parent
24b0f030c2
commit
c1e8d37dec
@ -528,6 +528,7 @@ noinst_HEADERS = \
|
||||
include/uofs.h\
|
||||
include/utime.h\
|
||||
include/dlist.h\
|
||||
include/elist.h\
|
||||
include/xlist.h\
|
||||
mds/locks.c\
|
||||
mds/locks.h\
|
||||
|
185
src/include/elist.h
Normal file
185
src/include/elist.h
Normal file
@ -0,0 +1,185 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab
|
||||
/*
|
||||
* Ceph - scalable distributed file system
|
||||
*
|
||||
* Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2.1, as published by the Free Software
|
||||
* Foundation. See file COPYING.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ELIST_H
|
||||
#define __ELIST_H
|
||||
|
||||
/*
|
||||
* elist: embedded list.
|
||||
*
|
||||
* requirements:
|
||||
* - elist<T>::item be embedded in the parent class
|
||||
* - items are _always_ added to the list via the same elist<T>::item at the same
|
||||
* fixed offset in the class.
|
||||
* - begin(), front(), back() methods take the member offset as an argument for traversal.
|
||||
*
|
||||
*/
|
||||
|
||||
#define member_offset(cls, member) ((size_t)(&((cls*)1)->member) - 1)
|
||||
|
||||
template<typename T>
|
||||
class elist {
|
||||
public:
|
||||
struct item {
|
||||
item *_prev, *_next;
|
||||
|
||||
item(T i=0) : _prev(this), _next(this) {}
|
||||
~item() {
|
||||
assert(!is_on_list());
|
||||
}
|
||||
// no copying!
|
||||
item(const item& other);
|
||||
const item& operator= (const item& right);
|
||||
|
||||
|
||||
bool empty() { return _prev == this; }
|
||||
bool is_on_list() { return !empty(); }
|
||||
|
||||
bool remove_myself() {
|
||||
if (_next == this) {
|
||||
assert(_prev == this);
|
||||
return false;
|
||||
}
|
||||
_next->_prev = _prev;
|
||||
_prev->_next = _next;
|
||||
_prev = _next = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
void insert_after(item *other) {
|
||||
assert(other->empty());
|
||||
other->_prev = this;
|
||||
other->_next = _next;
|
||||
_next->_prev = other;
|
||||
_next = other;
|
||||
}
|
||||
void insert_before(item *other) {
|
||||
assert(other->empty());
|
||||
other->_next = this;
|
||||
other->_prev = _prev;
|
||||
_prev->_next = other;
|
||||
_prev = other;
|
||||
}
|
||||
|
||||
T get_item(size_t offset) {
|
||||
assert(offset);
|
||||
return (T)(((char *)this) - offset);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
item _head;
|
||||
size_t item_offset;
|
||||
|
||||
public:
|
||||
elist(size_t o) : _head(NULL), item_offset(o) {}
|
||||
~elist() {
|
||||
assert(_head.empty());
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
return _head.empty();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
while (!_head.empty())
|
||||
remove(front());
|
||||
}
|
||||
|
||||
void push_front(item *item) {
|
||||
if (!item->empty())
|
||||
item->remove_myself();
|
||||
_head.insert_after(item);
|
||||
}
|
||||
void push_back(item *item) {
|
||||
if (!item->empty())
|
||||
item->remove_myself();
|
||||
_head.insert_before(item);
|
||||
}
|
||||
|
||||
T front(size_t o=0) {
|
||||
assert(!_head.empty());
|
||||
return _head._next->get_item(o ? o : item_offset);
|
||||
}
|
||||
T back(size_t o=0) {
|
||||
assert(!_head.empty());
|
||||
return _head._prev->get_item(o ? o : item_offset);
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
assert(!empty());
|
||||
_head._next->remove_myself();
|
||||
}
|
||||
void pop_back() {
|
||||
assert(!empty());
|
||||
_head._prev->remove_myself();
|
||||
}
|
||||
|
||||
enum mode_t {
|
||||
MAGIC, CURRENT, CACHE_NEXT
|
||||
};
|
||||
|
||||
class iterator {
|
||||
private:
|
||||
item *head;
|
||||
item *cur, *next;
|
||||
size_t item_offset;
|
||||
mode_t mode;
|
||||
public:
|
||||
iterator(item *h, size_t o, mode_t m) :
|
||||
head(h), cur(h->_next), next(cur->_next), item_offset(o),
|
||||
mode(m) {
|
||||
assert(item_offset > 0);
|
||||
}
|
||||
T operator*() {
|
||||
return cur->get_item(item_offset);
|
||||
}
|
||||
iterator& operator++() {
|
||||
assert(cur);
|
||||
assert(cur != head);
|
||||
if (mode == MAGIC) {
|
||||
// if 'cur' appears to be valid, use that. otherwise,
|
||||
// use cached 'next'.
|
||||
// this is a bit magic, and probably a bad idea... :/
|
||||
if (cur->empty())
|
||||
cur = next;
|
||||
else
|
||||
cur = cur->_next;
|
||||
} else if (mode == CURRENT)
|
||||
cur = cur->_next;
|
||||
else if (mode == CACHE_NEXT)
|
||||
cur = next;
|
||||
else
|
||||
assert(0);
|
||||
next = cur->_next;
|
||||
return *this;
|
||||
}
|
||||
bool end() {
|
||||
return cur == head;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin(size_t o=0) {
|
||||
return iterator(&_head, o ? o : item_offset, MAGIC);
|
||||
}
|
||||
iterator begin_use_current(size_t o=0) {
|
||||
return iterator(&_head, o ? o : item_offset, CURRENT);
|
||||
}
|
||||
iterator begin_cache_next(size_t o=0) {
|
||||
return iterator(&_head, o ? o : item_offset, CACHE_NEXT);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user