| /* |
| * Copyright (c) 2001-2005 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef __RES_LIST_HH__ |
| #define __RES_LIST_HH__ |
| |
| #include "base/cprintf.hh" |
| #include <assert.h> |
| |
| #define DEBUG_REMOVE 0 |
| |
| #define DEBUG_MEMORY 0 |
| //#define DEBUG_MEMORY DEBUG |
| |
| class res_list_base |
| { |
| #if DEBUG_MEMORY |
| protected: |
| static long long allocated_elements; |
| static long long allocated_lists; |
| |
| public: |
| long long get_elements(void) { |
| return allocated_elements; |
| } |
| long long get_lists(void) { |
| return allocated_lists; |
| } |
| |
| #endif |
| }; |
| |
| #if DEBUG_MEMORY |
| extern void what_the(void); |
| #endif |
| |
| template<class T> |
| class res_list : public res_list_base |
| { |
| public: |
| class iterator; |
| |
| class res_element |
| { |
| res_element *next; |
| res_element *prev; |
| T *data; |
| bool allocate_data; |
| |
| public: |
| // always adds to the END of the list |
| res_element(res_element *_prev, bool allocate); |
| ~res_element(); |
| void dump(void); |
| |
| friend class res_list<T>; |
| friend class res_list<T>::iterator; |
| }; |
| |
| class iterator |
| { |
| private: |
| res_element *p; |
| |
| friend class res_list<T>; |
| |
| public: |
| // Constructors |
| iterator(res_element *q) : p(q) {} |
| iterator(void) { p=0; }; |
| |
| void dump(void); |
| T* data_ptr(void); |
| res_element *res_el_ptr(void) { return p;} |
| void point_to(T &d) { p->data = &d; } |
| |
| iterator next(void) { return iterator(p->next); } |
| iterator prev(void) { return iterator(p->prev); } |
| bool operator== (iterator x) { return (x.p == this->p); } |
| bool operator != (iterator x) { return (x.p != this->p); } |
| T &operator * (void) { return *(p->data); } |
| T* operator -> (void) { return p->data; } |
| bool isnull(void) { return (p==0); } |
| bool notnull(void) { return (p!=0); } |
| }; |
| |
| private: |
| iterator unused_elements; |
| iterator head_ptr; |
| iterator tail_ptr; |
| |
| unsigned base_elements; |
| unsigned extra_elements; |
| unsigned active_elements; |
| bool allocate_storage; |
| unsigned build_size; |
| |
| int remove_count; |
| |
| // |
| // Allocate new elements, and assign them to the unused_elements |
| // list. |
| // |
| unsigned allocate_elements(unsigned num, bool allocate_storage); |
| |
| public: |
| // |
| // List Constructor |
| // |
| res_list(unsigned size, bool alloc_storage = false, |
| unsigned build_sz = 5); |
| |
| // |
| // List Destructor |
| // |
| ~res_list(); |
| |
| iterator head(void) {return head_ptr;}; |
| iterator tail(void) {return tail_ptr;}; |
| |
| unsigned num_free(void) { return size() - count(); } |
| unsigned size(void) { return base_elements + extra_elements; } |
| unsigned count(void) { return active_elements; } |
| bool empty(void) { return count() == 0; } |
| bool full(void); |
| |
| // |
| // Insert with data copy |
| // |
| iterator insert_after(iterator prev, T *d); |
| iterator insert_after(iterator prev, T &d); |
| iterator insert_before(iterator prev, T *d); |
| iterator insert_before(iterator prev, T &d); |
| |
| // |
| // Insert new list element (no data copy) |
| // |
| iterator insert_after(iterator prev); |
| iterator insert_before(iterator prev); |
| |
| iterator add_tail(T *d) { return insert_after(tail_ptr, d); } |
| iterator add_tail(T &d) { return insert_after(tail_ptr, d); } |
| iterator add_tail(void) { return insert_after(tail_ptr); } |
| iterator add_head(T *d) { return insert_before(head_ptr, d); } |
| iterator add_head(T &d) { return insert_before(head_ptr, d); } |
| iterator add_head(void) { return insert_before(head_ptr); } |
| |
| iterator remove(iterator q); |
| iterator remove_head(void) {return remove(head_ptr);} |
| iterator remove_tail(void) {return remove(tail_ptr);} |
| |
| bool in_list(iterator j); |
| void free_extras(void); |
| void clear(void); |
| void dump(void); |
| void raw_dump(void); |
| }; |
| |
| template <class T> |
| inline |
| res_list<T>::res_element::res_element(res_element *_prev, bool allocate) |
| { |
| allocate_data = allocate; |
| prev = _prev; |
| next = 0; |
| |
| if (prev) |
| prev->next = this; |
| |
| if (allocate) |
| data = new T; |
| else |
| data = 0; |
| |
| #if DEBUG_MEMORY |
| ++allocated_elements; |
| #endif |
| } |
| |
| template <class T> |
| inline |
| res_list<T>::res_element::~res_element(void) |
| { |
| if (prev) |
| prev->next = next; |
| |
| if (next) |
| next->prev = prev; |
| |
| if (allocate_data) |
| delete data; |
| |
| #if DEBUG_MEMORY |
| --allocated_elements; |
| #endif |
| } |
| |
| template <class T> |
| inline void |
| res_list<T>::res_element::dump(void) |
| { |
| cprintf(" prev = %#x\n", prev); |
| cprintf(" next = %#x\n", next); |
| cprintf(" data = %#x\n", data); |
| } |
| |
| template <class T> |
| inline void |
| res_list<T>::iterator::dump(void) |
| { |
| if (p && p->data) |
| p->data->dump(); |
| else { |
| if (!p) |
| cprintf(" Null Pointer\n"); |
| else |
| cprintf(" Null 'data' Pointer\n"); |
| } |
| } |
| |
| template <class T> |
| inline T * |
| res_list<T>::iterator::data_ptr(void) |
| { |
| if (p) |
| return p->data; |
| else |
| return 0; |
| } |
| |
| |
| // |
| // Allocate new elements, and assign them to the unused_elements |
| // list. |
| // |
| template <class T> |
| inline unsigned |
| res_list<T>::allocate_elements(unsigned num, bool allocate_storage) |
| { |
| res_element *pnew, *plast = 0, *pfirst=0; |
| |
| for (int i=0; i<num; ++i) { |
| pnew = new res_element(plast, allocate_storage); |
| if (i==0) |
| pfirst = pnew; |
| plast = pnew; |
| } |
| |
| if (unused_elements.notnull()) { |
| // Add these new elements to the front of the list |
| plast->next = unused_elements.res_el_ptr(); |
| unused_elements.res_el_ptr()->prev = plast; |
| } |
| |
| unused_elements = iterator(pfirst); |
| |
| return num; |
| } |
| |
| template <class T> |
| inline |
| res_list<T>::res_list(unsigned size, bool alloc_storage, unsigned build_sz) |
| { |
| #if DEBUG_MEMORY |
| ++allocated_lists; |
| #endif |
| extra_elements = 0; |
| active_elements = 0; |
| build_size = build_sz; |
| allocate_storage = alloc_storage; |
| remove_count = 0; |
| |
| // Create the new elements |
| base_elements = allocate_elements(size, alloc_storage); |
| |
| // The list of active elements |
| head_ptr = iterator(0); |
| tail_ptr = iterator(0); |
| } |
| |
| // |
| // List Destructor |
| // |
| template <class T> |
| inline |
| res_list<T>::~res_list(void) |
| { |
| iterator n; |
| |
| #if DEBUG_MEMORY |
| --allocated_lists; |
| #endif |
| |
| // put everything into the unused list |
| clear(); |
| |
| // rudely delete all the res_elements |
| for (iterator p = unused_elements; |
| p.notnull(); |
| p = n) { |
| |
| n = p.next(); |
| |
| // delete the res_element |
| // (it will take care of deleting the data) |
| delete p.res_el_ptr(); |
| } |
| } |
| |
| template <class T> |
| inline bool |
| res_list<T>::full(void) |
| { |
| if (build_size) |
| return false; |
| else |
| return unused_elements.isnull(); |
| } |
| |
| // |
| // Insert with data copy |
| // |
| template <class T> |
| inline typename res_list<T>::iterator |
| res_list<T>::insert_after(iterator prev, T *d) |
| { |
| iterator p; |
| |
| if (!allocate_storage) |
| this->panic("Can't copy data... not allocating storage"); |
| |
| p = insert_after(prev); |
| if (p.notnull()) |
| *p = *d; |
| |
| return p; |
| } |
| |
| |
| template <class T> |
| inline typename res_list<T>::iterator |
| res_list<T>::insert_after(iterator prev, T &d) |
| { |
| iterator p; |
| |
| p = insert_after(prev); |
| if (p.notnull()) { |
| |
| if (allocate_storage) { |
| // if we allocate storage, then copy the contents of the |
| // specified object to our object |
| *p = d; |
| } |
| else { |
| // if we don't allocate storage, then we just want to |
| // point to the specified object |
| p.point_to(d); |
| } |
| } |
| |
| return p; |
| } |
| |
| |
| template <class T> |
| inline typename res_list<T>::iterator |
| res_list<T>::insert_after(iterator prev) |
| { |
| |
| #if DEBUG_MEMORY |
| if (active_elements > 2*base_elements) { |
| what_the(); |
| } |
| #endif |
| |
| // If we have no unused elements, make some more |
| if (unused_elements.isnull()) { |
| |
| if (build_size == 0) { |
| return 0; // No space left, and can't allocate more.... |
| } |
| |
| extra_elements += allocate_elements(build_size, allocate_storage); |
| } |
| |
| // grab the first unused element |
| res_element *p = unused_elements.res_el_ptr(); |
| |
| unused_elements = unused_elements.next(); |
| |
| ++active_elements; |
| |
| // Insert the new element |
| if (head_ptr.isnull()) { |
| // |
| // Special case #1: Empty List |
| // |
| head_ptr = p; |
| tail_ptr = p; |
| p->prev = 0; |
| p->next = 0; |
| } |
| else if (prev.isnull()) { |
| // |
| // Special case #2: Insert at head |
| // |
| |
| // our next ptr points to old head element |
| p->next = head_ptr.res_el_ptr(); |
| |
| // our element becomes the new head element |
| head_ptr = p; |
| |
| // no previous element for the head |
| p->prev = 0; |
| |
| // old head element points back to this element |
| p->next->prev = p; |
| } |
| else if (prev.next().isnull()) { |
| // |
| // Special case #3 Insert at tail |
| // |
| |
| // our prev pointer points to old tail element |
| p->prev = tail_ptr.res_el_ptr(); |
| |
| // our element becomes the new tail |
| tail_ptr = p; |
| |
| // no next element for the tail |
| p->next = 0; |
| |
| // old tail element point to this element |
| p->prev->next = p; |
| } |
| else { |
| // |
| // Normal insertion (after prev) |
| // |
| p->prev = prev.res_el_ptr(); |
| p->next = prev.next().res_el_ptr(); |
| |
| prev.res_el_ptr()->next = p; |
| p->next->prev = p; |
| } |
| |
| return iterator(p); |
| } |
| |
| template <class T> |
| inline typename res_list<T>::iterator |
| res_list<T>::insert_before(iterator next, T &d) |
| { |
| iterator p; |
| |
| p = insert_before(next); |
| if (p.notnull()) { |
| |
| if (allocate_storage) { |
| // if we allocate storage, then copy the contents of the |
| // specified object to our object |
| *p = d; |
| } |
| else { |
| // if we don't allocate storage, then we just want to |
| // point to the specified object |
| p.point_to(d); |
| } |
| } |
| |
| return p; |
| } |
| |
| |
| template <class T> |
| inline typename res_list<T>::iterator |
| res_list<T>::insert_before(iterator next) |
| { |
| |
| #if DEBUG_MEMORY |
| if (active_elements > 2*base_elements) { |
| what_the(); |
| } |
| #endif |
| |
| // If we have no unused elements, make some more |
| if (unused_elements.isnull()) { |
| |
| if (build_size == 0) { |
| return 0; // No space left, and can't allocate more.... |
| } |
| |
| extra_elements += allocate_elements(build_size, allocate_storage); |
| } |
| |
| // grab the first unused element |
| res_element *p = unused_elements.res_el_ptr(); |
| |
| unused_elements = unused_elements.next(); |
| |
| ++active_elements; |
| |
| // Insert the new element |
| if (head_ptr.isnull()) { |
| // |
| // Special case #1: Empty List |
| // |
| head_ptr = p; |
| tail_ptr = p; |
| p->prev = 0; |
| p->next = 0; |
| } |
| else if (next.isnull()) { |
| // |
| // Special case #2 Insert at tail |
| // |
| |
| // our prev pointer points to old tail element |
| p->prev = tail_ptr.res_el_ptr(); |
| |
| // our element becomes the new tail |
| tail_ptr = p; |
| |
| // no next element for the tail |
| p->next = 0; |
| |
| // old tail element point to this element |
| p->prev->next = p; |
| } |
| else if (next.prev().isnull()) { |
| // |
| // Special case #3: Insert at head |
| // |
| |
| // our next ptr points to old head element |
| p->next = head_ptr.res_el_ptr(); |
| |
| // our element becomes the new head element |
| head_ptr = p; |
| |
| // no previous element for the head |
| p->prev = 0; |
| |
| // old head element points back to this element |
| p->next->prev = p; |
| } |
| else { |
| // |
| // Normal insertion (before next) |
| // |
| p->next = next.res_el_ptr(); |
| p->prev = next.prev().res_el_ptr(); |
| |
| next.res_el_ptr()->prev = p; |
| p->prev->next = p; |
| } |
| |
| return iterator(p); |
| } |
| |
| |
| template <class T> |
| inline typename res_list<T>::iterator |
| res_list<T>::remove(iterator q) |
| { |
| res_element *p = q.res_el_ptr(); |
| iterator n = 0; |
| |
| // Handle the special cases |
| if (active_elements == 1) { // This is the only element |
| head_ptr = 0; |
| tail_ptr = 0; |
| } |
| else if (q == head_ptr) { // This is the head element |
| head_ptr = q.next(); |
| head_ptr.res_el_ptr()->prev = 0; |
| |
| n = head_ptr; |
| } |
| else if (q == tail_ptr) { // This is the tail element |
| tail_ptr = q.prev(); |
| tail_ptr.res_el_ptr()->next = 0; |
| } |
| else { // This is between two elements |
| p->prev->next = p->next; |
| p->next->prev = p->prev; |
| |
| // Get the "next" element for return |
| n = p->next; |
| } |
| |
| --active_elements; |
| |
| // Put this element back onto the unused list |
| p->next = unused_elements.res_el_ptr(); |
| p->prev = 0; |
| if (p->next) { // NULL if unused list is empty |
| p->next->prev = p; |
| } |
| |
| if (!allocate_storage) { |
| p->data = 0; |
| } |
| |
| unused_elements = q; |
| |
| // A little "garbage collection" |
| if (++remove_count > 10) { |
| // free_extras(); |
| remove_count = 0; |
| } |
| |
| #if DEBUG_REMOVE |
| unsigned unused_count = 0; |
| for (iterator i=unused_elements; |
| i.notnull(); |
| i = i.next()) { |
| |
| ++unused_count; |
| } |
| |
| assert((active_elements+unused_count) == (base_elements+extra_elements)); |
| #endif |
| |
| return iterator(n); |
| } |
| |
| |
| template <class T> |
| inline bool |
| res_list<T>::in_list(iterator j) |
| { |
| iterator i; |
| |
| for (i=head(); i.notnull(); i=i.next()) { |
| if (j.res_el_ptr() == i.res_el_ptr()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| template <class T> |
| inline void |
| res_list<T>::free_extras(void) |
| { |
| unsigned num_unused = base_elements + extra_elements - active_elements; |
| unsigned to_free = extra_elements; |
| res_element *p; |
| |
| |
| if (extra_elements != 0) { |
| // |
| // Free min(extra_elements, # unused elements) |
| // |
| if (extra_elements > num_unused) { |
| to_free = num_unused; |
| } |
| |
| p = unused_elements.res_el_ptr(); |
| for (int i=0; i<to_free; ++i) { |
| res_element *q = p->next; |
| |
| delete p; |
| |
| p = q; |
| } |
| |
| // update the unused element pointer to point to the first |
| // element that wasn't deleted. |
| unused_elements = iterator(p); |
| |
| // Update the number of extra elements |
| extra_elements -= to_free; |
| } |
| |
| return; |
| } |
| |
| |
| template <class T> |
| inline void |
| res_list<T>::clear(void) |
| { |
| iterator i,n; |
| |
| for (i=head_ptr; i.notnull(); i=n) { |
| n = i.next(); |
| remove(i); |
| } |
| |
| free_extras(); |
| } |
| |
| template <class T> |
| inline void |
| res_list<T>::dump(void) |
| { |
| for (iterator i=head(); !i.isnull(); i=i.next()) |
| i->dump(); |
| } |
| |
| template <class T> |
| inline void |
| res_list<T>::raw_dump(void) |
| { |
| int j = 0; |
| res_element *p; |
| for (iterator i=head(); !i.isnull(); i=i.next()) { |
| cprintf("Element %d:\n", j); |
| |
| if (i.notnull()) { |
| p = i.res_el_ptr(); |
| cprintf(" points to res_element @ %#x\n", p); |
| p->dump(); |
| cprintf(" Data Element:\n"); |
| i->dump(); |
| } |
| else { |
| cprintf(" NULL iterator!\n"); |
| } |
| |
| ++j; |
| } |
| |
| } |
| |
| #endif // __RES_LIST_HH__ |