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 }