1 /* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22 
23 #ifndef BOUNDED_QUEUE_C_INCLUDED
24 #define BOUNDED_QUEUE_C_INCLUDED
25 
26 #include <string.h>
27 #include "my_global.h"
28 #include "my_base.h"
29 #include "my_sys.h"
30 #include "queues.h"
31 #include "test_utils.h"
32 
33 /**
34   A priority queue with a fixed, limited size.
35 
36   This is a wrapper on top of QUEUE and the queue_xxx() functions.
37   It keeps the top-N elements which are inserted.
38 
39   Elements of type Element_type are pushed into the queue.
40   For each element, we call a user-supplied Key_generator::make_sortkey(),
41   to generate a key of type Key_type for the element.
42   Instances of Key_type are compared with the user-supplied compare_function.
43 
44   The underlying QUEUE implementation needs one extra element for replacing
45   the lowest/highest element when pushing into a full queue.
46 
47   Pointers to the top-N elements are stored in the sort_keys array given
48   to the init() function below. To access elements in sorted order, use
49   repeated calls to pop(), or, simply sort the array and access it sequentially.
50   The pop() interface is for unit testing only.
51  */
52 template<typename Element_type, typename Key_type, typename Key_generator>
53 class Bounded_QUEUE
54 {
55 public:
Bounded_QUEUE()56   Bounded_QUEUE()
57   {
58     memset(&m_queue, 0, sizeof(m_queue));
59   }
60 
~Bounded_QUEUE()61   ~Bounded_QUEUE()
62   {
63     delete_queue(&m_queue);
64   }
65 
66   /**
67      Function for comparing two keys.
68      @param  n Pointer to number of bytes to compare.
69      @param  a First key.
70      @param  b Second key.
71      @retval -1, 0, or 1 depending on whether the left argument is
72              less than, equal to, or greater than the right argument.
73    */
74   typedef int (*compare_function)(size_t *n, Key_type *a, Key_type *b);
75 
76   /**
77     Initialize the queue.
78 
79     @param max_elements   The size of the queue.
80     @param max_at_top     Set to true if you want biggest element on top.
81            false: We keep the n largest elements.
82                   pop() will return the smallest key in the result set.
83            true:  We keep the n smallest elements.
84                   pop() will return the largest key in the result set.
85     @param compare        Compare function for elements, takes 3 arguments.
86            If NULL, we use get_ptr_compare(sort_param->compare_length()).
87     @param sort_param     Sort parameters. We call sort_param->make_sortkey()
88                           to generate keys for elements.
89     @param[in,out] sort_keys Array of keys to sort.
90                              Must be initialized by caller.
91                              Will be filled with pointers to the top-N elements.
92 
93     @retval 0 OK, 1 Could not allocate memory.
94 
95     We do *not* take ownership of any of the input pointer arguments.
96    */
97   int init(ha_rows max_elements, bool max_at_top,
98            compare_function compare,
99            Key_generator *sort_param,
100            Key_type *sort_keys);
101 
102   /**
103     Pushes an element on the queue.
104     If the queue is already full, we discard one element.
105     Calls m_sort_param::make_sortkey() to generate a key for the element.
106 
107     @param element        The element to be pushed.
108    */
109   void push(Element_type element);
110 
111   /**
112     Removes the top element from the queue.
113 
114     @retval Pointer to the (key of the) removed element.
115 
116     @note This function is for unit testing, where we push elements into to the
117           queue, and test that the appropriate keys are retained.
118           Interleaving of push() and pop() operations has not been tested.
119    */
pop()120   Key_type *pop()
121   {
122     // Don't return the extra element to the client code.
123     if (queue_is_full((&m_queue)))
124       queue_remove(&m_queue, 0);
125     assert(m_queue.elements > 0);
126     if (m_queue.elements == 0)
127       return NULL;
128     return reinterpret_cast<Key_type*>(queue_remove(&m_queue, 0));
129   }
130 
131   /**
132     The number of elements in the queue.
133    */
num_elements()134   uint num_elements() const { return m_queue.elements; }
135 
136   /**
137     Is the queue initialized?
138    */
is_initialized()139   bool is_initialized() const { return m_queue.max_elements > 0; }
140 
141 private:
142   Key_type          *m_sort_keys;
143   size_t             m_compare_length;
144   Key_generator     *m_sort_param;
145   st_queue           m_queue;
146 };
147 
148 
149 template<typename Element_type, typename Key_type, typename Key_generator>
150 int Bounded_QUEUE<Element_type, Key_type, Key_generator>
init(ha_rows max_elements,bool max_at_top,compare_function compare,Key_generator * sort_param,Key_type * sort_keys)151   ::init(ha_rows max_elements,
152          bool max_at_top,
153          compare_function compare,
154          Key_generator *sort_param,
155          Key_type *sort_keys)
156 {
157   assert(sort_keys != NULL);
158 
159   m_sort_keys=      sort_keys;
160   m_compare_length= sort_param->compare_length();
161   m_sort_param=     sort_param;
162   // init_queue() takes an uint, and also does (max_elements + 1)
163   if (max_elements >= (UINT_MAX - 1))
164     return 1;
165   if (compare == NULL)
166     compare=
167       reinterpret_cast<compare_function>
168         (my_testing::get_ptr_compare(m_compare_length));
169 
170   DBUG_EXECUTE_IF("bounded_queue_init_fail",
171                   DBUG_SET("+d,simulate_out_of_memory"););
172 
173   // We allocate space for one extra element, for replace when queue is full.
174   return init_queue(&m_queue, (uint) max_elements + 1,
175                     0, max_at_top,
176                     reinterpret_cast<queue_compare>(compare),
177                     &m_compare_length);
178 }
179 
180 
181 template<typename Element_type, typename Key_type, typename Key_generator>
182 void Bounded_QUEUE<Element_type, Key_type, Key_generator>
push(Element_type element)183   ::push(Element_type element)
184 {
185   assert(is_initialized());
186   if (queue_is_full((&m_queue)))
187   {
188     // Replace top element with new key, and re-order the queue.
189     Key_type *pq_top= reinterpret_cast<Key_type *>(queue_top(&m_queue));
190     m_sort_param->make_sortkey(*pq_top, element);
191     queue_replaced(&m_queue);
192   }
193   else
194   {
195     // Insert new key into the queue.
196     m_sort_param->make_sortkey(m_sort_keys[m_queue.elements], element);
197     queue_insert(&m_queue,
198                  reinterpret_cast<uchar*>(&m_sort_keys[m_queue.elements]));
199   }
200 }
201 
202 
203 #endif  // BOUNDED_QUEUE_C_INCLUDED
204