1 /******************************************************************************* 2 3 Fixed size memory-based ring queue with a fixed element size. 4 5 TODO: 6 Update the classes in this module to be able to use a custom allocater 7 like the constructors of FlexibleByteRingQueue allow. 8 9 Copyright: 10 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 11 All rights reserved. 12 13 License: 14 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 15 Alternatively, this file may be distributed under the terms of the Tango 16 3-Clause BSD License (see LICENSE_BSD.txt for details). 17 18 *******************************************************************************/ 19 20 module ocean.util.container.queue.FixedRingQueue; 21 22 23 24 25 import ocean.util.container.queue.model.IRingQueue; 26 27 import ocean.util.container.queue.model.IQueue; 28 29 import ocean.util.container.queue.model.IByteQueue; 30 31 import ocean.util.container.mem.MemManager; 32 33 version (unittest) import ocean.core.Test; 34 35 import ocean.meta.types.Qualifiers; 36 37 38 /******************************************************************************* 39 40 Ring queue for elements of type T, T must be a value type. 41 42 *******************************************************************************/ 43 44 class FixedRingQueue ( T ) : FixedRingQueueBase!(IQueue!(T)) 45 { 46 import ocean.core.Verify; 47 48 /*************************************************************************** 49 50 Constructor 51 52 Params: 53 max_items = maximum number of elements in queue 54 55 ***************************************************************************/ 56 57 public this ( size_t max_items ) 58 { 59 super(max_items, T.sizeof); 60 } 61 62 /*************************************************************************** 63 64 Pushes an element into the queue. 65 66 Params: 67 element = element to push (will be left unchanged) 68 69 Returns: 70 true on success or false if the queue is full. 71 72 ***************************************************************************/ 73 74 public bool push ( T element ) 75 { 76 T* element_in_queue = this.push(); 77 78 if (element_in_queue) 79 { 80 *element_in_queue = element; 81 return true; 82 } 83 else 84 { 85 return false; 86 } 87 } 88 89 /*************************************************************************** 90 91 Pushes an element into the queue and returns a pointer to that element. 92 The value of the element must then be copied to the location pointed to 93 before calling push() or pop() the next time. 94 95 Returns: 96 pointer to the element pushed into the queue or null if the queue is 97 full. 98 99 ***************************************************************************/ 100 101 public T* push ( ) 102 { 103 return cast (T*) super.push_().ptr; 104 } 105 106 /*************************************************************************** 107 108 Pops an element from the queue. 109 110 Params: 111 element = destination for popped element, will be changed only if 112 the return value is true. 113 114 Returns: 115 true on success or false if the queue is empty. 116 117 ***************************************************************************/ 118 119 public bool pop ( ref T element ) 120 { 121 T* element_in_queue = this.pop(); 122 123 if (element_in_queue) 124 { 125 element = *element_in_queue; 126 return true; 127 } 128 else 129 { 130 return false; 131 } 132 } 133 134 135 /*************************************************************************** 136 137 Pops an element from the queue and returns a pointer to that element. 138 The value of the element must then be copied from the location pointed 139 to before calling push() or pop() the next time. 140 141 Returns: 142 pointer to the element popped from the queue or null if the queue is 143 empty. 144 145 ***************************************************************************/ 146 147 public T* pop ( ) 148 { 149 return cast (T*) super.pop_().ptr; 150 } 151 } 152 153 /******************************************************************************* 154 155 Ring queue for raw element data. 156 157 *******************************************************************************/ 158 159 class FixedByteRingQueue : FixedRingQueueBase!(IByteQueue) 160 { 161 import ocean.core.Verify; 162 163 /*************************************************************************** 164 165 Constructor 166 167 Params: 168 element_size = element size in bytes 169 max_items = maximum number of elements in queue 170 171 ***************************************************************************/ 172 173 this ( size_t element_size, size_t max_items ) 174 { 175 super(max_items, element_size); 176 } 177 178 /*************************************************************************** 179 180 Pushes an element into the queue. 181 182 Params: 183 element = element to push (will be left unchanged) 184 185 Returns: 186 true on success or false if the queue is full. 187 188 ***************************************************************************/ 189 190 bool push ( in void[] element ) 191 { 192 verify (element.length == super.element_size, "element size mismatch"); 193 194 auto element_in_queue = super.push_(); 195 196 if (element_in_queue) 197 { 198 const(void)[] element_ = element; 199 element_in_queue[] = element_[]; 200 return true; 201 } 202 else 203 { 204 return false; 205 } 206 } 207 208 /*************************************************************************** 209 210 Pushes an element into the queue and returns a slice to that element. 211 The value of the element must then be copied to the sliced location 212 before the next push() or pop() is called. 213 214 Returns: 215 slice to the element pushed into the queue or null if the queue is 216 full. 217 218 ***************************************************************************/ 219 220 void[] push ( size_t ignored = 0 ) 221 { 222 return super.push_(); 223 } 224 225 /*************************************************************************** 226 227 Pops an element from the queue and returns a slice to that element. 228 The value of the element must then be copied from the sliced location 229 before the next push() or pop() is called. 230 231 Returns: 232 pointer to the element popped from the queue or null if the queue is 233 empty. 234 235 ***************************************************************************/ 236 237 void[] pop ( ) 238 { 239 return super.pop_(); 240 } 241 242 void[] peek ( ) 243 { 244 return super.peek_(); 245 } 246 247 /*************************************************************************** 248 249 Pops an element from the queue and copies the value to element. 250 251 Params: 252 element = destination buffer, the length must be the element size. 253 Will be changed only if the return value is true. 254 255 Returns: 256 true on success or false if the queue is empty. 257 258 ***************************************************************************/ 259 260 bool pop ( void[] element ) 261 { 262 verify (element.length == super.element_size, "element size mismatch"); 263 264 auto element_in_queue = super.pop_(); 265 266 if (element_in_queue) 267 { 268 element[] = element_in_queue[]; 269 return true; 270 } 271 else 272 { 273 return false; 274 } 275 } 276 } 277 278 /******************************************************************************* 279 280 Ring queue base class. 281 282 *******************************************************************************/ 283 284 abstract class FixedRingQueueBase ( IBaseQueue ) : IRingQueue!(IBaseQueue) 285 { 286 import ocean.core.Verify; 287 288 /*************************************************************************** 289 290 Maximum number of elements in queue 291 292 ***************************************************************************/ 293 294 protected size_t max_items; 295 296 /*************************************************************************** 297 298 Element size in bytes 299 300 ***************************************************************************/ 301 302 protected size_t element_size; 303 304 /*************************************************************************** 305 306 Consistency check 307 308 ***************************************************************************/ 309 310 invariant ( ) 311 { 312 assert (super.items <= this.max_items); 313 314 // Both read and write position must be integer multiples of the element 315 // size. 316 317 assert (!(super.write_to % this.element_size)); 318 assert (!(super.read_from % this.element_size)); 319 320 // If the queue is empty or full, the read position must equal the write 321 // position. If the read position equals the write position, the queue 322 // must be empty or full. 323 324 assert ((0 < super.items && super.items < this.max_items) ^ (super.read_from == super.write_to)); 325 326 assert (super.write_to < super.data.length); 327 assert (super.read_from < super.data.length); 328 329 size_t used_space = super.items * this.element_size; 330 331 if (super.write_to < super.read_from) 332 { 333 assert (used_space == super.data.length - (super.read_from - super.write_to)); 334 } 335 else if (super.write_to > super.read_from) 336 { 337 assert (used_space == super.write_to - super.read_from); 338 } 339 else if (super.items) 340 { 341 assert (super.items == this.max_items); 342 assert (used_space == super.data.length); 343 } 344 else 345 { 346 assert (!used_space); 347 } 348 } 349 350 /*************************************************************************** 351 352 Constructor 353 354 Params: 355 element_size = element size in bytes 356 max_items = maximum number of elements in queue 357 358 ***************************************************************************/ 359 360 this ( size_t max_items, size_t element_size ) 361 { 362 verify (element_size > 0); 363 verify (max_items > 0); 364 365 super((this.element_size = element_size) * (this.max_items = max_items)); 366 } 367 368 /*************************************************************************** 369 370 Returns: 371 number of bytes stored in queue 372 373 ***************************************************************************/ 374 375 override ulong used_space ( ) 376 { 377 return super.items * this.element_size; 378 } 379 380 /*************************************************************************** 381 382 Returns: 383 maximum number of elements that could be held in queue. 384 385 ***************************************************************************/ 386 387 size_t maxItems ( ) 388 { 389 return this.max_items; 390 } 391 392 393 /*************************************************************************** 394 395 Finds out whether another item will fit 396 397 Params: 398 bytes = size of item to check, ignored as sizes is fixed 399 400 Returns: 401 true if the item fits, else false 402 403 ***************************************************************************/ 404 405 public bool willFit ( size_t bytes = 0) 406 { 407 return super.items < this.max_items; 408 } 409 410 411 /*************************************************************************** 412 413 Pushes an element into the queue and returns a slice to that element. 414 The value of the element must then be copied to the sliced location 415 before the next push_() or pop_() is called. 416 417 Returns: 418 slice to the element pushed into the queue or null if the queue is 419 full. 420 421 ***************************************************************************/ 422 423 protected void[] push_ ( ) 424 out (element) 425 { 426 assert (!element || element.length == this.element_size); 427 } 428 do 429 { 430 if (super.items < this.max_items) 431 { 432 super.items++; 433 434 return this.getElement(super.write_to); 435 } 436 else 437 { 438 return null; 439 } 440 } 441 442 /*************************************************************************** 443 444 Pops an element from the queue and returns a slice to that element. 445 The value of the element must then be copied from the sliced location 446 before the next push() or pop() is called. 447 448 Returns: 449 slice of the element popped from the queue or null if the queue is 450 empty. 451 452 ***************************************************************************/ 453 454 protected void[] pop_ ( ) 455 out (element) 456 { 457 assert (!element || element.length == this.element_size); 458 } 459 do 460 { 461 if (super.items) 462 { 463 super.items--; 464 465 return this.getElement(super.read_from); 466 } 467 else 468 { 469 return null; 470 } 471 } 472 473 474 protected void[] peek_ ( ) 475 out (element) 476 { 477 assert (!element || element.length == this.element_size); 478 } 479 do 480 { 481 if (super.items) 482 { 483 auto read_pos = super.read_from; 484 485 return this.getElement(read_pos); 486 } 487 else 488 { 489 return null; 490 } 491 } 492 493 /*************************************************************************** 494 495 Slices the element at position pos in super.data and increments pos by 496 the element size. If pos reaches the end of data, it is reset (wrapped 497 around) to 0. 498 499 Params: 500 pos = position in super.data 501 502 Returns: 503 slice to the element at the requested position in super.data. 504 505 ***************************************************************************/ 506 507 private void[] getElement ( ref size_t pos ) 508 { 509 size_t end = pos + this.element_size; 510 511 auto chunk = super.data[pos .. end]; 512 513 pos = end; 514 515 if (pos == super.data.length) 516 { 517 pos = 0; 518 } 519 520 return chunk; 521 } 522 } 523 524 unittest 525 { 526 static size_t pos ( size_t n ) 527 { 528 return n * int.sizeof; 529 } 530 531 scope queue = new FixedRingQueue!(int)(3); 532 533 int n; 534 535 test (queue.is_empty); 536 test (!queue.pop()); 537 test (queue.is_empty); 538 539 test (queue.push(1)); 540 test (queue.get_write_to == pos(1)); 541 test (queue.get_read_from == pos(0)); 542 test (!queue.is_empty); 543 544 test (queue.pop(n)); 545 test (n == 1); 546 547 test (queue.is_empty); 548 test (!queue.pop()); 549 test (queue.get_write_to == queue.get_read_from); 550 551 test (queue.push(2)); 552 test (!queue.is_empty); 553 test (queue.push(3)); 554 test (queue.push(4)); 555 test (!queue.push(5)); 556 test (queue.get_write_to == queue.get_read_from); 557 558 test (queue.pop(n)); 559 test (n == 2); 560 561 test (queue.pop(n)); 562 test (n == 3); 563 564 test (queue.pop(n)); 565 test (n == 4); 566 567 test (queue.is_empty); 568 test (!queue.pop()); 569 test (queue.get_write_to == queue.get_read_from); 570 571 test (queue.push(5)); 572 573 test (queue.pop(n)); 574 test (n == 5); 575 576 test (queue.is_empty); 577 test (!queue.pop()); 578 test (queue.get_write_to == queue.get_read_from); 579 }