| 
									
										
										
										
											2011-02-23 10:52:22 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version 2 | 
					
						
							| 
									
										
										
										
											2008-04-16 22:40:48 +00:00
										 |  |  |  * of the License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program 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 this program; if not, write to the Free Software Foundation, | 
					
						
							| 
									
										
										
										
											2010-02-12 13:34:04 +00:00
										 |  |  |  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-02-27 20:37:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 08:08:12 +11:00
										 |  |  | /** \file
 | 
					
						
							|  |  |  |  * \ingroup bli | 
					
						
							| 
									
										
										
										
											2014-08-07 14:42:47 +10:00
										 |  |  |  * | 
					
						
							|  |  |  |  * \brief A generic structure queue | 
					
						
							|  |  |  |  * (a queue for fixed length generally small) structures. | 
					
						
							| 
									
										
										
										
											2011-02-27 20:37:56 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | #include <string.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "MEM_guardedalloc.h"
 | 
					
						
							| 
									
										
										
										
											2013-03-09 05:35:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | #include "BLI_gsqueue.h"
 | 
					
						
							| 
									
										
										
										
											2013-09-01 03:43:10 +00:00
										 |  |  | #include "BLI_strict_flags.h"
 | 
					
						
							| 
									
										
										
										
											2020-03-19 09:33:03 +01:00
										 |  |  | #include "BLI_utildefines.h"
 | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | /* target chunk size: 64kb */ | 
					
						
							|  |  |  | #define CHUNK_SIZE_DEFAULT (1 << 16)
 | 
					
						
							|  |  |  | /* ensure we get at least this many elems per chunk */ | 
					
						
							|  |  |  | #define CHUNK_ELEM_MIN 32
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct QueueChunk { | 
					
						
							|  |  |  |   struct QueueChunk *next; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   char data[0]; | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _GSQueue { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   struct QueueChunk *chunk_first; /* first active chunk to pop from */ | 
					
						
							|  |  |  |   struct QueueChunk *chunk_last;  /* flast active chunk to push onto */ | 
					
						
							|  |  |  |   struct QueueChunk *chunk_free;  /* free chunks to reuse */ | 
					
						
							|  |  |  |   size_t chunk_first_index;       /* index into 'chunk_first' */ | 
					
						
							|  |  |  |   size_t chunk_last_index;        /* index into 'chunk_last' */ | 
					
						
							|  |  |  |   size_t chunk_elem_max;          /* number of elements per chunk */ | 
					
						
							|  |  |  |   size_t elem_size;               /* memory size of elements */ | 
					
						
							|  |  |  |   size_t totelem;                 /* total number of elements */ | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 16:35:17 +02:00
										 |  |  | static void *queue_get_first_elem(GSQueue *queue) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return ((char *)(queue)->chunk_first->data) + ((queue)->elem_size * (queue)->chunk_first_index); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *queue_get_last_elem(GSQueue *queue) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return ((char *)(queue)->chunk_last->data) + ((queue)->elem_size * (queue)->chunk_last_index); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * \return number of elements per chunk, optimized for slop-space. | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | static size_t queue_chunk_elem_max_calc(const size_t elem_size, size_t chunk_size) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   /* get at least this number of elems per chunk */ | 
					
						
							|  |  |  |   const size_t elem_size_min = elem_size * CHUNK_ELEM_MIN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   BLI_assert((elem_size != 0) && (chunk_size != 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (UNLIKELY(chunk_size <= elem_size_min)) { | 
					
						
							|  |  |  |     chunk_size <<= 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* account for slop-space */ | 
					
						
							|  |  |  |   chunk_size -= (sizeof(struct QueueChunk) + MEM_SIZE_OVERHEAD); | 
					
						
							| 
									
										
										
										
											2018-06-17 16:32:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   return chunk_size / elem_size; | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | GSQueue *BLI_gsqueue_new(const size_t elem_size) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   GSQueue *queue = MEM_callocN(sizeof(*queue), "BLI_gsqueue_new"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   queue->chunk_elem_max = queue_chunk_elem_max_calc(elem_size, CHUNK_SIZE_DEFAULT); | 
					
						
							|  |  |  |   queue->elem_size = elem_size; | 
					
						
							|  |  |  |   /* force init */ | 
					
						
							|  |  |  |   queue->chunk_last_index = queue->chunk_elem_max - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return queue; | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | static void queue_free_chunk(struct QueueChunk *data) | 
					
						
							| 
									
										
										
										
											2018-06-17 16:32:54 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   while (data) { | 
					
						
							|  |  |  |     struct QueueChunk *data_next = data->next; | 
					
						
							|  |  |  |     MEM_freeN(data); | 
					
						
							|  |  |  |     data = data_next; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-01-22 11:10:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * Free the queue's data and the queue itself | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | void BLI_gsqueue_free(GSQueue *queue) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   queue_free_chunk(queue->chunk_first); | 
					
						
							|  |  |  |   queue_free_chunk(queue->chunk_free); | 
					
						
							|  |  |  |   MEM_freeN(queue); | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * Copies the source value onto the end of the queue | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \note This copies #GSQueue.elem_size bytes from \a src, | 
					
						
							|  |  |  |  * (the pointer itself is not stored). | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * \param src: source data to be copied to the queue. | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | void BLI_gsqueue_push(GSQueue *queue, const void *src) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   queue->chunk_last_index++; | 
					
						
							|  |  |  |   queue->totelem++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (UNLIKELY(queue->chunk_last_index == queue->chunk_elem_max)) { | 
					
						
							|  |  |  |     struct QueueChunk *chunk; | 
					
						
							|  |  |  |     if (queue->chunk_free) { | 
					
						
							|  |  |  |       chunk = queue->chunk_free; | 
					
						
							|  |  |  |       queue->chunk_free = chunk->next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       chunk = MEM_mallocN(sizeof(*chunk) + (queue->elem_size * queue->chunk_elem_max), __func__); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     chunk->next = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (queue->chunk_last == NULL) { | 
					
						
							|  |  |  |       queue->chunk_first = chunk; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       queue->chunk_last->next = chunk; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |     queue->chunk_last = chunk; | 
					
						
							|  |  |  |     queue->chunk_last_index = 0; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   BLI_assert(queue->chunk_last_index < queue->chunk_elem_max); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Return last of queue */ | 
					
						
							| 
									
										
										
										
											2019-10-01 16:35:17 +02:00
										 |  |  |   memcpy(queue_get_last_elem(queue), src, queue->elem_size); | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * Retrieves and removes the first element from the queue. | 
					
						
							|  |  |  |  * The value is copies to \a dst, which must be at least \a elem_size bytes. | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * Does not reduce amount of allocated memory. | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | void BLI_gsqueue_pop(GSQueue *queue, void *dst) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   BLI_assert(BLI_gsqueue_is_empty(queue) == false); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 16:35:17 +02:00
										 |  |  |   memcpy(dst, queue_get_first_elem(queue), queue->elem_size); | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   queue->chunk_first_index++; | 
					
						
							|  |  |  |   queue->totelem--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (UNLIKELY(queue->chunk_first_index == queue->chunk_elem_max || queue->totelem == 0)) { | 
					
						
							|  |  |  |     struct QueueChunk *chunk_free = queue->chunk_first; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |     queue->chunk_first = queue->chunk_first->next; | 
					
						
							|  |  |  |     queue->chunk_first_index = 0; | 
					
						
							|  |  |  |     if (queue->chunk_first == NULL) { | 
					
						
							|  |  |  |       queue->chunk_last = NULL; | 
					
						
							|  |  |  |       queue->chunk_last_index = queue->chunk_elem_max - 1; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |     chunk_free->next = queue->chunk_free; | 
					
						
							|  |  |  |     queue->chunk_free = chunk_free; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | size_t BLI_gsqueue_len(const GSQueue *queue) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   return queue->totelem; | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |  * Returns true if the queue is empty, false otherwise | 
					
						
							| 
									
										
										
										
											2013-03-24 01:51:54 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  | bool BLI_gsqueue_is_empty(const GSQueue *queue) | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-01 11:13:28 +02:00
										 |  |  |   return (queue->chunk_first == NULL); | 
					
						
							| 
									
										
										
										
											2002-10-12 11:37:38 +00:00
										 |  |  | } |