]> git.sesse.net Git - casparcg/blobdiff - dependencies64/tbb/include/tbb/internal/_flow_graph_tagged_buffer_impl.h
Updated some libraries to newer versions and/or versions compiled for vc12 (freeimage...
[casparcg] / dependencies64 / tbb / include / tbb / internal / _flow_graph_tagged_buffer_impl.h
index 8b87fd14fe9a0bb34e4e30417b6c7a7ae674d49c..8c13eb59211757f54cb4aa45f2e396f232e2bdf2 100644 (file)
@@ -1,33 +1,25 @@
 /*
-    Copyright 2005-2011 Intel Corporation.  All Rights Reserved.
-
-    This file is part of Threading Building Blocks.
-
-    Threading Building Blocks is free software; you can redistribute it
-    and/or modify it under the terms of the GNU General Public License
-    version 2 as published by the Free Software Foundation.
-
-    Threading Building Blocks is distributed in the hope that it will be
-    useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Threading Building Blocks; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-    As a special exception, you may use this file as part of a free software
-    library without restriction.  Specifically, if other files instantiate
-    templates or use macros or inline functions from this file, or you compile
-    this file and link it with other files to produce an executable, this
-    file does not by itself cause the resulting executable to be covered by
-    the GNU General Public License.  This exception does not however
-    invalidate any other reasons why the executable file might be covered by
-    the GNU General Public License.
+    Copyright 2005-2014 Intel Corporation.  All Rights Reserved.
+
+    This file is part of Threading Building Blocks. Threading Building Blocks is free software;
+    you can redistribute it and/or modify it under the terms of the GNU General Public License
+    version 2  as  published  by  the  Free Software Foundation.  Threading Building Blocks is
+    distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
+    implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+    See  the GNU General Public License for more details.   You should have received a copy of
+    the  GNU General Public License along with Threading Building Blocks; if not, write to the
+    Free Software Foundation, Inc.,  51 Franklin St,  Fifth Floor,  Boston,  MA 02110-1301 USA
+
+    As a special exception,  you may use this file  as part of a free software library without
+    restriction.  Specifically,  if other files instantiate templates  or use macros or inline
+    functions from this file, or you compile this file and link it with other files to produce
+    an executable,  this file does not by itself cause the resulting executable to be covered
+    by the GNU General Public License. This exception does not however invalidate any other
+    reasons why the executable file might be covered by the GNU General Public License.
 */
 
 // tagged buffer that can expand, and can support as many deletions as additions
-// list-based, with elements of list held in std::vector (for destruction management),
+// list-based, with elements of list held in array (for destruction management),
 // multiplicative hashing (like ets).  No synchronization built-in.
 //
 
 #error Do not #include this internal file directly; use public TBB headers instead.
 #endif
 
+// included in namespace tbb::flow::interface7::internal
+
+template<typename T, typename U, size_t NoTagMark>
+struct otherData {
+    T t;
+    U next;
+    otherData() : t(NoTagMark), next(NULL) {}
+};
+
 template<typename TagType, typename ValueType, size_t NoTagMark>
-struct buffer_element {
-    TagType t;
-    ValueType v;
-    buffer_element *next;
-    buffer_element() : t(NoTagMark), next(NULL) {}
+struct buffer_element_type {
+    // the second parameter below is void * because we can't forward-declare the type
+    // itself, so we just reinterpret_cast below.
+    typedef typename aligned_pair<ValueType, otherData<TagType, void *, NoTagMark> >::type type;
 };
 
 template
@@ -51,126 +51,175 @@ template
      typename TagType, 
      typename ValueType, 
      size_t   NoTagMark = 0,
-     typename Allocator=tbb::cache_aligned_allocator< buffer_element<TagType,ValueType,NoTagMark> >
+     typename Allocator=tbb::cache_aligned_allocator< typename buffer_element_type<TagType, ValueType, NoTagMark>::type >
     >
 class tagged_buffer {
 public:
     static const size_t INITIAL_SIZE = 8;  // initial size of the hash pointer table
     static const TagType NO_TAG = TagType(NoTagMark);
     typedef ValueType value_type;
-    typedef buffer_element<TagType,ValueType, NO_TAG> element_type;
+    typedef typename buffer_element_type<TagType, ValueType, NO_TAG>::type element_type;
     typedef value_type *pointer_type;
-    typedef std::vector<element_type, Allocator> list_array_type;
-    typedef typename Allocator::template rebind<element_type*>::other pointer_array_allocator_type;
-    typedef typename Allocator::template rebind<list_array_type>::other list_array_allocator;
+    typedef element_type *list_array_type;  // array we manage manually
+    typedef list_array_type *pointer_array_type;
+    typedef typename Allocator::template rebind<list_array_type>::other pointer_array_allocator_type;
+    typedef typename Allocator::template rebind<element_type>::other elements_array_allocator;
 private:
-
     size_t my_size;
     size_t nelements;
-    element_type** array;
-    std::vector<element_type, Allocator> *lists;
+    pointer_array_type pointer_array;    // pointer_array[my_size]
+    list_array_type elements_array;      // elements_array[my_size / 2]
     element_type* free_list;
 
     size_t mask() { return my_size - 1; }
 
-// #define ABYSMAL 1
     static size_t hash(TagType t) {
-#if ABYSMAL
-        return (size_t)1;
-#else
-#if __TBB_WORDSIZE == 4
-        return uintptr_t(t)*0x9E3779B9;
-#else
-        return uintptr_t(t)*0x9E3779B97F4A7C15;
-#endif
-#endif
+        return uintptr_t(t)*tbb::internal::select_size_t_constant<0x9E3779B9,0x9E3779B97F4A7C15ULL>::value;
     }
 
-    void set_up_free_list( element_type **p_free_list, list_array_type *la, size_t sz) {
+    void set_up_free_list( element_type **p_free_list, list_array_type la, size_t sz) {
         for(size_t i=0; i < sz - 1; ++i ) {  // construct free list
-            (*la)[i].next = &((*la)[i+1]);
-            (*la)[i].t = NO_TAG;
+            la[i].second.next = &(la[i+1]);
+            la[i].second.t = NO_TAG;
         }
-        (*la)[sz-1].next = NULL;
-        *p_free_list = &((*la)[0]);
+        la[sz-1].second.next = NULL;
+        *p_free_list = &(la[0]);
     }
 
+    // cleanup for exceptions
+    struct DoCleanup {
+        pointer_array_type *my_pa;
+        list_array_type *my_elements;
+        size_t my_size;
+
+        DoCleanup(pointer_array_type &pa, list_array_type &my_els, size_t sz) :
+            my_pa(&pa), my_elements(&my_els), my_size(sz) {  }
+        ~DoCleanup() {
+            if(my_pa) {
+                size_t dont_care = 0;
+                internal_free_buffer(*my_pa, *my_elements, my_size, dont_care);
+            }
+        }
+    };
+
+    // exception-safety requires we do all the potentially-throwing operations first
     void grow_array() {
-        // make the pointer array larger
-        element_type **new_array;
-        element_type **old_array = array;
-        size_t old_size = my_size;
-        my_size *=2;
-        new_array = pointer_array_allocator_type().allocate(my_size);
-        for(size_t i=0; i < my_size; ++i) new_array[i] = NULL;
-        list_array_type *new_list_array = new list_array_type(old_size, element_type(), Allocator());
-        set_up_free_list(&free_list, new_list_array, old_size );
-
-        for(size_t i=0; i < old_size; ++i) {
-            for( element_type* op = old_array[i]; op; op = op->next) {
-                internal_tagged_insert(new_array, my_size, op->t, op->v);
+        size_t new_size = my_size*2;
+        size_t new_nelements = nelements;  // internal_free_buffer zeroes this
+        list_array_type new_elements_array = NULL;
+        pointer_array_type new_pointer_array = NULL;
+        list_array_type new_free_list = NULL;
+        {
+            DoCleanup my_cleanup(new_pointer_array, new_elements_array, new_size);
+            new_elements_array = elements_array_allocator().allocate(my_size);
+            new_pointer_array = pointer_array_allocator_type().allocate(new_size);
+            for(size_t i=0; i < new_size; ++i) new_pointer_array[i] = NULL;
+            set_up_free_list(&new_free_list, new_elements_array, my_size );
+
+            for(size_t i=0; i < my_size; ++i) {
+                for( element_type* op = pointer_array[i]; op; op = (element_type *)(op->second.next)) {
+                    value_type *ov = reinterpret_cast<value_type *>(&(op->first));
+                    // could have std::move semantics
+                    internal_tagged_insert(new_pointer_array, new_size, new_free_list, op->second.t, *ov);
+                }
             }
+            my_cleanup.my_pa = NULL;
+            my_cleanup.my_elements = NULL;
         }
-        pointer_array_allocator_type().deallocate(old_array, old_size);
 
-        delete lists;  // destroy and deallocate instead
-        array = new_array;
-        lists = new_list_array;
+        internal_free_buffer(pointer_array, elements_array, my_size, nelements);
+        free_list = new_free_list;
+        pointer_array = new_pointer_array;
+        elements_array = new_elements_array;
+        my_size = new_size;
+        nelements = new_nelements;
     }
 
-    void internal_tagged_insert( element_type **ar, size_t sz, TagType t, value_type v) {
-        size_t l_mask = sz-1;
+    // v should have perfect forwarding if std::move implemented.
+    // we use this method to move elements in grow_array, so can't use class fields
+    void internal_tagged_insert( element_type **p_pointer_array, size_t p_sz, list_array_type &p_free_list,
+            const TagType t, const value_type &v) {
+        size_t l_mask = p_sz-1;
         size_t h = hash(t) & l_mask;
-        __TBB_ASSERT(free_list, "Error: free list not set up.");
-        element_type* my_elem = free_list; free_list = free_list->next;
-        my_elem->t = t;
-        my_elem->v = v;
-        my_elem->next = ar[h];
-        ar[h] = my_elem;
+        __TBB_ASSERT(p_free_list, "Error: free list not set up.");
+        element_type* my_elem = p_free_list; p_free_list = (element_type *)(p_free_list->second.next);
+        my_elem->second.t = t;
+        (void) new(&(my_elem->first)) value_type(v);
+        my_elem->second.next = p_pointer_array[h];
+        p_pointer_array[h] = my_elem;
+    }
+
+    void internal_initialize_buffer() {
+        pointer_array = pointer_array_allocator_type().allocate(my_size);
+        for(size_t i = 0; i < my_size; ++i) pointer_array[i] = NULL;
+        elements_array = elements_array_allocator().allocate(my_size / 2);
+        set_up_free_list(&free_list, elements_array, my_size / 2);
+    }
+
+    // made static so an enclosed class can use to properly dispose of the internals
+    static void internal_free_buffer( pointer_array_type &pa, list_array_type &el, size_t &sz, size_t &ne ) {
+        if(pa) {
+            for(size_t i = 0; i < sz; ++i ) {
+                element_type *p_next;
+                for( element_type *p = pa[i]; p; p = p_next) {
+                    p_next = (element_type *)p->second.next;
+                    value_type *vp = reinterpret_cast<value_type *>(&(p->first));
+                    vp->~value_type();
+                }
+            }
+            pointer_array_allocator_type().deallocate(pa, sz); 
+            pa = NULL;
+        }
+        // Separate test (if allocation of pa throws, el may be allocated.
+        // but no elements will be constructed.)
+        if(el) {
+            elements_array_allocator().deallocate(el, sz / 2);
+            el = NULL;
+        }
+        sz = INITIAL_SIZE;
+        ne = 0;
     }
 
 public:
     tagged_buffer() : my_size(INITIAL_SIZE), nelements(0) {
-        array = pointer_array_allocator_type().allocate(my_size);
-        for(size_t i = 0; i < my_size; ++i) array[i] = NULL;
-        lists = new list_array_type(INITIAL_SIZE/2, element_type(), Allocator());
-        set_up_free_list(&free_list, lists, INITIAL_SIZE/2);
+        internal_initialize_buffer();
     }
 
     ~tagged_buffer() {
-        if(array) {
-            pointer_array_allocator_type().deallocate(array, my_size); 
-        }
-        if(lists) {
-            delete lists;
-        }
+        internal_free_buffer(pointer_array, elements_array, my_size, nelements);
+    }
+
+    void reset() {
+        internal_free_buffer(pointer_array, elements_array, my_size, nelements);
+        internal_initialize_buffer();
     }
 
-    bool tagged_insert(TagType t, value_type v) {
+    bool tagged_insert(const TagType t, const value_type &v) {
         pointer_type p;
         if(tagged_find_ref(t, p)) {
-            *p = v;  // replace the value
+            p->~value_type();
+            (void) new(p) value_type(v);  // copy-construct into the space
             return false;
         }
         ++nelements;
         if(nelements*2 > my_size) grow_array();
-        internal_tagged_insert(array, my_size, t, v);
+        internal_tagged_insert(pointer_array, my_size, free_list, t, v);
         return true;
     }
 
     // returns reference to array element.v
-    bool tagged_find_ref(TagType t, pointer_type &v) {
+    bool tagged_find_ref(const TagType t, pointer_type &v) {
         size_t i = hash(t) & mask();
-        for(element_type* p = array[i]; p; p = p->next) {
-            if(p->t == t) {
-                v = &(p->v);
+        for(element_type* p = pointer_array[i]; p; p = (element_type *)(p->second.next)) {
+            if(p->second.t == t) {
+                v = reinterpret_cast<pointer_type>(&(p->first));
                 return true;
             }
         }
         return false;
     }
 
-    bool tagged_find( TagType t, value_type &v) {
+    bool tagged_find( const TagType t, value_type &v) {
         value_type *p;
         if(tagged_find_ref(t, p)) {
             v = *p;
@@ -180,15 +229,17 @@ public:
             return false;
     }
 
-    void tagged_delete(TagType t) {
+    void tagged_delete(const TagType t) {
         size_t h = hash(t) & mask();
         element_type* prev = NULL;
-        for(element_type* p = array[h]; p; prev = p, p = p->next) {
-            if(p->t == t) {
-                p->t = NO_TAG;
-                if(prev) prev->next = p->next;
-                else array[h] = p->next;
-                p->next = free_list;
+        for(element_type* p = pointer_array[h]; p; prev = p, p = (element_type *)(p->second.next)) {
+            if(p->second.t == t) {
+                value_type *vp = reinterpret_cast<value_type *>(&(p->first));
+                vp->~value_type();
+                p->second.t = NO_TAG;
+                if(prev) prev->second.next = p->second.next;
+                else pointer_array[h] = (element_type *)(p->second.next);
+                p->second.next = free_list;
                 free_list = p;
                 --nelements;
                 return;
@@ -196,17 +247,5 @@ public:
         }
         __TBB_ASSERT(false, "tag not found for delete");
     }
-
-    // search for v in the array; if found {set t, return true} else return false
-    // we use this in join_node_FE to find if a tag's items are all available.
-    bool find_value_tag( TagType &t, value_type v) {
-        for(size_t i= 0; i < my_size / 2; ++i) {  // remember the vector is half the size of the hash array
-            if( (*lists)[i].t != NO_TAG && (*lists)[i].v == v) {
-                t = (*lists)[i].t;
-                return true;
-            }
-        }
-        return false;
-    }
 };
 #endif // __TBB__flow_graph_tagged_buffer_impl_H