1 /******************************************************************************* 2 3 Wrapper for a common boilerplate to imitate queue using dynamic array. 4 5 Provides IQueue!(T) interfaces, stores items internally using dynamic 6 array of items. Will grow indefinitely as long as there is any spare 7 memory. Popped items will initially be marked as "free" and occasionally 8 whole array will be shifted to optimized used memory space. 9 10 Copyright: 11 Copyright (c) 2018 dunnhumby Germany GmbH. 12 All rights reserved. 13 14 License: 15 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 16 Alternatively, this file may be distributed under the terms of the Tango 17 3-Clause BSD License (see LICENSE_BSD.txt for details). 18 19 *******************************************************************************/ 20 21 module ocean.util.container.queue.DynamicQueue; 22 23 import ocean.core.Buffer; 24 import ocean.core.array.Mutation : removeShift; 25 import ocean.util.container.queue.model.IQueue; 26 27 version (unittest) 28 { 29 import ocean.core.Test; 30 } 31 32 /// ditto 33 class DynamicQueue ( T ) : IQueue!(T) 34 { 35 /// Underlying item storage 36 private Buffer!(T) buffer; 37 /// Marks oldest stored item offset (== the one that will be popped next) 38 private size_t oldest; 39 40 /// When set to 'true`, will automatically shift all items in the underlying 41 /// array to utilize freed space. It is a recommended default. 42 public bool auto_shrink = true; 43 44 /*************************************************************************** 45 46 Removes all items from the queue. 47 48 ***************************************************************************/ 49 50 override public void clear ( ) 51 { 52 this.buffer.reset(); 53 this.oldest = 0; 54 } 55 56 /*************************************************************************** 57 58 Reserves space for an element of the size T.sizeof at the queue and 59 returns a pointer to it. 60 The value of the element must then be copied to the location pointed to 61 before calling push() or pop() the next time. 62 63 Returns: 64 pointer to the element pushed into the queue or null if the queue is 65 full. 66 67 ***************************************************************************/ 68 69 override public T* push ( ) 70 { 71 if (this.auto_shrink) 72 { 73 if (this.oldest > this.buffer.length / 3) 74 this.shrink(); 75 } 76 77 this.buffer.length = this.buffer.length + 1; 78 return &this.buffer[][$-1]; 79 } 80 81 /*************************************************************************** 82 83 Pushes an element into the queue. 84 85 Params: 86 element = element to push (will be left unchanged) 87 88 Returns: 89 true on success or false if the queue is full. 90 91 ***************************************************************************/ 92 93 override public bool push ( T element ) 94 { 95 *this.push() = element; 96 return true; 97 } 98 99 /*************************************************************************** 100 101 Pops an element from the queue and returns a pointer to that element. 102 The value of the element must then be copied from the location pointed 103 to before calling push() or pop() the next time. 104 105 Returns: 106 pointer to the element popped from the queue or null if the queue is 107 empty. 108 109 ***************************************************************************/ 110 111 override public T* pop ( ) 112 { 113 if (this.buffer.length <= this.oldest) 114 return null; 115 116 return &this.buffer[][this.oldest++]; 117 } 118 119 /*************************************************************************** 120 121 Returns: 122 the number of items in the queue 123 124 ***************************************************************************/ 125 126 override public size_t length ( ) 127 { 128 return this.buffer.length - this.oldest; 129 } 130 131 /*************************************************************************** 132 133 Returns: 134 number of bytes stored in queue 135 136 ***************************************************************************/ 137 138 override public ulong used_space ( ) 139 { 140 return this.length * T.sizeof; 141 } 142 143 /*************************************************************************** 144 145 Returns: 146 number of bytes free in queue 147 148 ***************************************************************************/ 149 150 override public ulong free_space ( ) 151 { 152 return this.oldest * T.sizeof; 153 } 154 155 /*************************************************************************** 156 157 Returns: 158 total number of bytes used by queue (used space + free space) 159 160 ***************************************************************************/ 161 162 override public ulong total_space ( ) 163 { 164 return this.buffer.length * T.sizeof; 165 } 166 167 /*************************************************************************** 168 169 Tells whether the queue is empty. 170 171 Returns: 172 true if the queue is empty 173 174 ***************************************************************************/ 175 176 public bool is_empty ( ) 177 { 178 return this.oldest == this.buffer.length; 179 } 180 181 /*************************************************************************** 182 183 Finds out whether the provided number of bytes will fit in the queue. 184 185 Params: 186 bytes = size of item to check 187 188 Returns: 189 true if the bytes fits, else false 190 191 ***************************************************************************/ 192 193 public bool willFit ( size_t bytes ) 194 { 195 return true; 196 } 197 198 /*************************************************************************** 199 200 Shifts lefts currently used slots of the underlying array to optimize 201 memory usage. 202 203 ***************************************************************************/ 204 205 public void shrink () 206 { 207 removeShift(this.buffer, 0, this.oldest); 208 this.oldest = 0; 209 } 210 } 211 212 /// 213 unittest 214 { 215 auto queue = new DynamicQueue!(int); 216 queue.push(1); 217 queue.push(2); 218 queue.push(3); 219 test!("==")(*queue.pop(), 1); 220 test!("==")(*queue.pop(), 2); 221 test!("==")(*queue.pop(), 3); 222 } 223 224 unittest 225 { 226 static struct S { int field; } 227 auto queue = new DynamicQueue!(S); 228 229 queue.shrink(); 230 231 queue.push(S(1)); 232 test!("==")(queue.length, 1); 233 234 queue.clear(); 235 test!("==")(queue.length, 0); 236 237 test!("is")(queue.pop(), null); 238 queue.push(S(2)); 239 test!("==")(*queue.pop(), S(2)); 240 241 test!("==")(queue.used_space(), 0); 242 for (int i; i < 100; ++i) 243 queue.push(S(i)); 244 for (int i; i < 10; ++i) 245 queue.pop(); 246 test!("==")(queue.length(), 90); 247 test!("==")(queue.used_space(), 90 * S.sizeof); 248 test!("==")(queue.free_space(), 10 * S.sizeof); 249 test!("==")(queue.total_space(), 100 * S.sizeof); 250 queue.shrink(); 251 test!("==")(queue.total_space(), 90 * S.sizeof); 252 test!("==")(*queue.pop(), S(10)); 253 254 test!("==")(queue.is_empty(), false); 255 test!("==")(queue.willFit(size_t.max), true); 256 }