1 // libTorrent - BitTorrent library
2 // Copyright (C) 2005-2011, Jari Sundell
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 //
18 // In addition, as a special exception, the copyright holders give
19 // permission to link the code of portions of this program with the
20 // OpenSSL library under certain conditions as described in each
21 // individual source file, and distribute linked combinations
22 // including the two.
23 //
24 // You must obey the GNU General Public License in all respects for
25 // all of the code used other than OpenSSL.  If you modify file(s)
26 // with this exception, you may extend this exception to your version
27 // of the file(s), but you are not obligated to do so.  If you do not
28 // wish to do so, delete this exception statement from your version.
29 // If you delete this exception statement from all source files in the
30 // program, then also delete it here.
31 //
32 // Contact:  Jari Sundell <jaris@ifi.uio.no>
33 //
34 //           Skomakerveien 33
35 //           3185 Skoppum, NORWAY
36 
37 #ifndef LIBTORRENT_QUEUE_BUCKETS_H
38 #define LIBTORRENT_QUEUE_BUCKETS_H
39 
40 #include <algorithm>
41 #include <deque>
42 #include lt_tr1_functional
43 #include lt_tr1_array
44 
45 namespace torrent {
46 
47 template <typename Type, typename Constants>
48 class queue_buckets : private std::array<std::deque<Type>, Constants::bucket_count> {
49 public:
50   typedef std::deque<Type>                                queue_type;
51   typedef std::array<queue_type, Constants::bucket_count> base_type;
52 
53   typedef Constants constants;
54 
55   typedef typename queue_type::value_type value_type;
56   typedef typename queue_type::size_type size_type;
57   typedef typename queue_type::difference_type difference_type;
58 
59   typedef typename queue_type::iterator iterator;
60   typedef typename queue_type::const_iterator const_iterator;
61 
queue_size(int idx)62   size_type queue_size(int idx)  const { return queue_at(idx).size(); }
queue_empty(int idx)63   bool      queue_empty(int idx) const { return queue_at(idx).empty(); }
64 
65   bool             empty() const;
66 
begin(int idx)67   iterator         begin(int idx)       { return queue_at(idx).begin(); }
end(int idx)68   iterator         end(int idx)         { return queue_at(idx).end(); }
begin(int idx)69   const_iterator   begin(int idx) const { return queue_at(idx).begin(); }
end(int idx)70   const_iterator   end(int idx)   const { return queue_at(idx).end(); }
71 
front(int idx)72   value_type       front(int idx)       { return queue_at(idx).front(); }
back(int idx)73   value_type       back(int idx)        { return queue_at(idx).back(); }
front(int idx)74   const value_type front(int idx) const { return queue_at(idx).front(); }
back(int idx)75   const value_type back(int idx)  const { return queue_at(idx).back(); }
76 
77   void pop_front(int idx);
78   void pop_back(int idx);
79 
80   value_type pop_and_front(int idx);
81   value_type pop_and_back(int idx);
82 
83   void push_front(int idx, const value_type& value_type);
84   void push_back(int idx, const value_type& value_type);
85 
86   value_type take(int idx, iterator itr);
87 
88   void clear(int idx);
89   void destroy(int idx, iterator begin, iterator end);
90 
91   void move_to(int src_idx, iterator src_begin, iterator src_end, int dst_idx);
92   void move_all_to(int src_idx, int dst_idx);
93 
94 private:
queue_at(int idx)95   queue_type&       queue_at(int idx)       { return base_type::operator[](idx); }
queue_at(int idx)96   const queue_type& queue_at(int idx) const { return base_type::operator[](idx); }
97 };
98 
99 //
100 // Helper methods:
101 //
102 
103 template<typename QueueBucket, typename Ftor>
104 void
queue_bucket_for_all_in_queue(QueueBucket & queues,int idx,Ftor ftor)105 queue_bucket_for_all_in_queue(QueueBucket& queues, int idx, Ftor ftor) {
106   std::for_each(queues.begin(idx), queues.end(idx), ftor);
107 }
108 
109 template<typename QueueBucket, typename Ftor>
110 void
queue_bucket_for_all_in_queue(const QueueBucket & queues,int idx,Ftor ftor)111 queue_bucket_for_all_in_queue(const QueueBucket& queues, int idx, Ftor ftor) {
112   std::for_each(queues.begin(idx), queues.end(idx), ftor);
113 }
114 
115 template<typename QueueBucket, typename Ftor>
116 inline typename QueueBucket::iterator
queue_bucket_find_if_in_queue(QueueBucket & queues,int idx,Ftor ftor)117 queue_bucket_find_if_in_queue(QueueBucket& queues, int idx, Ftor ftor) {
118   return std::find_if(queues.begin(idx), queues.end(idx), ftor);
119 }
120 
121 template<typename QueueBucket, typename Ftor>
122 inline typename QueueBucket::const_iterator
queue_bucket_find_if_in_queue(const QueueBucket & queues,int idx,Ftor ftor)123 queue_bucket_find_if_in_queue(const QueueBucket& queues, int idx, Ftor ftor) {
124   return std::find_if(queues.begin(idx), queues.end(idx), ftor);
125 }
126 
127 template<typename QueueBucket, typename Ftor>
128 inline std::pair<int, typename QueueBucket::iterator>
queue_bucket_find_if_in_any(QueueBucket & queues,Ftor ftor)129 queue_bucket_find_if_in_any(QueueBucket& queues, Ftor ftor) {
130   for (int i = 0; i < QueueBucket::constants::bucket_count; i++) {
131     typename QueueBucket::iterator itr = std::find_if(queues.begin(i), queues.end(i), ftor);
132 
133     if (itr != queues.end(i))
134       return std::make_pair(i, itr);
135   }
136 
137   return std::make_pair(QueueBucket::constants::bucket_count,
138                         queues.end(QueueBucket::constants::bucket_count - 1));
139 }
140 
141 template<typename QueueBucket, typename Ftor>
142 inline std::pair<int, typename QueueBucket::const_iterator>
queue_bucket_find_if_in_any(const QueueBucket & queues,Ftor ftor)143 queue_bucket_find_if_in_any(const QueueBucket& queues, Ftor ftor) {
144   for (int i = 0; i < QueueBucket::constants::bucket_count; i++) {
145     typename QueueBucket::const_iterator itr = std::find_if(queues.begin(i), queues.end(i), ftor);
146 
147     if (itr != queues.end(i))
148       return std::make_pair(i, itr);
149   }
150 
151   return std::make_pair(QueueBucket::constants::bucket_count,
152                         queues.end(QueueBucket::constants::bucket_count - 1));
153 }
154 
155 //
156 // Implementation:
157 //
158 
159 // TODO: Consider renaming bucket to queue.
160 
161 // TODO: when adding removal/etc of element or ranges do logging on if
162 // it hit the first element or had to search.
163 
164 template <typename Type, typename Constants>
165 inline bool
empty()166 queue_buckets<Type, Constants>::empty() const {
167   for (int i = 0; i < constants::bucket_count; i++)
168     if (!queue_empty(i))
169       return false;
170 
171   return true;
172 }
173 
174 template <typename Type, typename Constants>
175 inline void
pop_front(int idx)176 queue_buckets<Type, Constants>::pop_front(int idx) {
177   queue_at(idx).pop_front();
178 
179   instrumentation_update(constants::instrumentation_removed[idx], 1);
180   instrumentation_update(constants::instrumentation_total[idx], -1);
181 }
182 
183 template <typename Type, typename Constants>
184 inline void
pop_back(int idx)185 queue_buckets<Type, Constants>::pop_back(int idx) {
186   queue_at(idx).pop_back();
187 
188   instrumentation_update(constants::instrumentation_removed[idx], 1);
189   instrumentation_update(constants::instrumentation_total[idx], -1);
190 }
191 
192 template <typename Type, typename Constants>
193 inline typename queue_buckets<Type, Constants>::value_type
pop_and_front(int idx)194 queue_buckets<Type, Constants>::pop_and_front(int idx) {
195   value_type v = queue_at(idx).front();
196   pop_front(idx);
197   return v;
198 }
199 
200 template <typename Type, typename Constants>
201 inline typename queue_buckets<Type, Constants>::value_type
pop_and_back(int idx)202 queue_buckets<Type, Constants>::pop_and_back(int idx) {
203   value_type v = queue_at(idx).back();
204   pop_back(idx);
205   return v;
206 }
207 
208 template <typename Type, typename Constants>
209 inline void
push_front(int idx,const value_type & value)210 queue_buckets<Type, Constants>::push_front(int idx, const value_type& value) {
211   queue_at(idx).push_front(value);
212 
213   instrumentation_update(constants::instrumentation_added[idx], 1);
214   instrumentation_update(constants::instrumentation_total[idx], 1);
215 }
216 
217 template <typename Type, typename Constants>
218 inline void
push_back(int idx,const value_type & value)219 queue_buckets<Type, Constants>::push_back(int idx, const value_type& value) {
220   queue_at(idx).push_back(value);
221 
222   instrumentation_update(constants::instrumentation_added[idx], 1);
223   instrumentation_update(constants::instrumentation_total[idx], 1);
224 }
225 
226 template <typename Type, typename Constants>
227 inline typename queue_buckets<Type, Constants>::value_type
take(int idx,iterator itr)228 queue_buckets<Type, Constants>::take(int idx, iterator itr) {
229   value_type v = *itr;
230   queue_at(idx).erase(itr);
231 
232   instrumentation_update(constants::instrumentation_removed[idx], 1);
233   instrumentation_update(constants::instrumentation_total[idx], -1);
234 
235   // TODO: Add 'taken' instrumentation.
236 
237   return v;
238 }
239 
240 template <typename Type, typename Constants>
241 inline void
clear(int idx)242 queue_buckets<Type, Constants>::clear(int idx) {
243   destroy(idx, begin(idx), end(idx));
244 }
245 
246 template <typename Type, typename Constants>
247 inline void
destroy(int idx,iterator begin,iterator end)248 queue_buckets<Type, Constants>::destroy(int idx, iterator begin, iterator end) {
249   difference_type difference = std::distance(begin, end);
250   instrumentation_update(constants::instrumentation_removed[idx], difference);
251   instrumentation_update(constants::instrumentation_total[idx], -difference);
252 
253   // Consider moving these to a temporary dequeue before releasing:
254   std::for_each(begin, end, std::function<void (value_type&)>(&constants::template destroy<value_type>));
255   queue_at(idx).erase(begin, end);
256 }
257 
258 template <typename Type, typename Constants>
259 inline void
move_to(int src_idx,iterator src_begin,iterator src_end,int dst_idx)260 queue_buckets<Type, Constants>::move_to(int src_idx, iterator src_begin, iterator src_end,
261                                         int dst_idx) {
262   difference_type difference = std::distance(src_begin, src_end);
263   instrumentation_update(constants::instrumentation_moved[src_idx], difference);
264   instrumentation_update(constants::instrumentation_total[src_idx], -difference);
265   instrumentation_update(constants::instrumentation_added[dst_idx], difference);
266   instrumentation_update(constants::instrumentation_total[dst_idx], difference);
267 
268   // TODO: Check for better move operations:
269   if (queue_at(dst_idx).empty() &&
270       src_begin == queue_at(src_idx).begin() &&
271       src_end == queue_at(src_idx).end()) {
272     queue_at(dst_idx).swap(queue_at(src_idx));
273   } else {
274     queue_at(dst_idx).insert(queue_at(dst_idx).end(), src_begin, src_end);
275     queue_at(src_idx).erase(src_begin, src_end);
276   }
277 }
278 
279 template <typename Type, typename Constants>
280 inline void
move_all_to(int src_idx,int dst_idx)281 queue_buckets<Type, Constants>::move_all_to(int src_idx, int dst_idx) {
282   move_to(src_idx, queue_at(src_idx).begin(), queue_at(src_idx).end(), dst_idx);
283 }
284 
285 }
286 
287 #endif // LIBTORRENT_QUEUE_BUCKETS_H
288