1 /**
2  * Priority Queue
3  * 
4  * Copyright:
5  * (C) 2012-2015 Tatsuhiro Tsujikawa
6  * (C) 2014-2015 Etienne Cimon
7  *
8  * License: 
9  * Distributed under the terms of the MIT license with an additional section 1.2 of the curl/libcurl project. 
10  * Consult the provided LICENSE.md file for details
11  */
12 module libhttp2.priority_queue;
13 
14 import libhttp2.types;
15 import libhttp2.frame : OutboundItem;
16 import memutils.utils;
17 
18 /// Implementation of priority queue
19 struct PriorityQueue
20 {
21 private:
22 	/// The pointer to the pointer to the item stored 
23 	OutboundItem[] m_queue;
24 
25 	/* The maximum number of items this pq can store. This is
26      automatically extended when length is reached to this value. */
27 	size_t m_capacity;
28 
29 public:
30 
31 	this(size_t capacity = 128)
32 	{
33 		m_capacity = capacity;
34 		m_queue = Mem.alloc!(OutboundItem[])(capacity);
35 		m_queue = m_queue.ptr[0 .. 0];
36 	}
37 
38 	/// Deallocates any resources allocated.  All stored items are freed by this function.
39 	void free()
40 	{
41 		while (!empty) 
42 		{
43 			OutboundItem item = top;
44 			item.free();
45 			Mem.free(item);
46 			pop();
47 		}
48 		if (m_queue)
49 			Mem.free(m_queue.ptr[0 .. m_capacity]);
50 		m_queue = null;
51 		m_capacity = 0;
52 	}
53 
54 
55 	/// Adds |item| to the priority queue
56 	void push(OutboundItem item)
57 	{
58 		if (m_capacity <= m_queue.length)
59 		{
60 			size_t len = m_queue.length;
61 			m_queue = Mem.realloc(m_queue[0 .. m_capacity], m_capacity * 2);
62 			m_capacity *= 2;
63 			m_queue = m_queue.ptr[0 .. len];
64 		}
65 		m_queue = m_queue.ptr[0 .. m_queue.length + 1];
66 		m_queue[$-1] = item;
67 		bubbleUp(m_queue.length - 1);
68 	}
69 
70 	
71 	/*
72 	 * Pops item at the top of the queue |pq|. The popped item is not
73 	 * freed by this function.
74 	 */
75 	void pop()
76 	{
77 		if (m_queue.length == 0) return;
78 
79 		m_queue[0] = m_queue[$ - 1];
80 		m_queue = m_queue.ptr[0 .. m_queue.length - 1];
81 		bubbleDown(0);
82 
83 	}
84 
85 	/*
86 	 * Returns item at the top of the queue |pq|. If the queue is empty,
87 	 * this function returns NULL.
88 	 */
89 	@property OutboundItem top()
90 	{
91 		if (length == 0) {
92 			return null;
93 		} else {
94 			return m_queue[0];
95 		}
96 	}
97 
98 	/*
99 	 * Returns true if the queue is empty.
100 	 */
101 	@property bool empty() const {
102 		return m_queue.length == 0;
103 	}
104 
105 
106 	/*
107 	 * Returns the number of items in the queue |pq|.
108 	 */
109 	@property size_t length() const {
110 		return m_queue.length;
111 	}
112 
113 	/// Iterates over each item in the PriorityQueue and reorders it
114 	int opApply(scope int delegate(OutboundItem ob) del) {
115 		
116 		if (m_queue.length == 0)
117 			return 0;
118 		
119 		// assume the ordering will change
120 		scope(exit) {
121 			for (size_t i = m_queue.length; i > 0; --i) {
122 				bubbleDown(i - 1);
123 			}
124 		}
125 		
126 		foreach (ob; m_queue) {
127 			if (auto ret = del(ob))
128 				return ret;
129 		}
130 		
131 		return 0;
132 	}
133 
134 	/// Iterates over each item in the PriorityQueue
135 	int opApply(scope int delegate(const OutboundItem ob) del) const {
136 		foreach (const ob; m_queue) {
137 			if (auto ret = del(ob))
138 				return ret;
139 		}
140 		return 0;
141 	}
142 
143 private:
144 	void swap(size_t i, size_t j) {
145 		OutboundItem t = m_queue[i];
146 		m_queue[i] = m_queue[j];
147 		m_queue[j] = t;
148 	}
149 
150 	void bubbleUp(size_t index) {
151 		if (index == 0) {
152 			return;
153 		} else {
154 			size_t parent = (index - 1) / 2;
155 			if (compare(m_queue[parent], m_queue[index]) > 0) {
156 				swap(parent, index);
157 				bubbleUp(parent);
158 			}
159 		}
160 	}
161 
162 	void bubbleDown(size_t index) {
163 		size_t lchild = index * 2 + 1;
164 		size_t minindex = index;
165 		size_t i, j;
166 		for (i = 0; i < 2; ++i) {
167 			j = lchild + i;
168 			if (j >= m_queue.length) {
169 				break;
170 			}
171 			if (compare(m_queue[minindex], m_queue[j]) > 0) {
172 				minindex = j;
173 			}
174 		}
175 		if (minindex != index) {
176 			swap(index, minindex);
177 			bubbleDown(minindex);
178 		}
179 	}
180 package:
181 	static int compare(in OutboundItem lhs, in OutboundItem rhs) 
182 	{
183 		if (lhs.cycle == rhs.cycle) {
184 			if (lhs.weight == rhs.weight) {
185 				return (lhs.seq < rhs.seq) ? -1 : ((lhs.seq > rhs.seq) ? 1 : 0);
186 			}
187 			
188 			/* Larger weight has higher precedence */
189 			return rhs.weight - lhs.weight;
190 		}
191 		
192 		return (lhs.cycle < rhs.cycle) ? -1 : 1;
193 	}
194 
195 }