--- A double queue. -- Taken from ***Programming in Lua*** [Queues and Double Queues](http://www.lua.org/pil/11.4.html) -- and modified to not allow nil values, and returns nil if @{pop_first} or @{pop_last} is used when the queue is empty. -- @module Queue -- @usage local Queue = require('stdlib/queue/queue') local Queue = {_module_name = 'Queue'} setmetatable(Queue, {__index = require('stdlib/core')}) local Is = require('stdlib/utils/is') local t_size = table.size --- Constructs a new Queue object. -- @return (@{Queue}) a new, empty queue function Queue.new() local queue = {first = 1, last = 0, objects = {}} setmetatable(queue, Queue._mt) return queue end Queue:set_caller(Queue.new) -- Allows queue[3] to return the item at queue.objects[3] local function __index(self, k) if Is.number(k) then return self.objects[k] else return rawget(self, k) or Queue[k] end end -- Allows queue() to pop_first and queue(data) to push_last local function __call(self, ...) if ... then return self.push(self, ...) else return self.pop(self) end end --- Load global.queue or queues during on_load, as metatables are not persisted. --

This is only needed if you are using the queue as an object and storing it in global. -- @param ... (@{Queue},...) -- @usage global.myqueue1 = Queue.new() -- global.myqueue2 = Queue.new() -- script.on_load(function() Queue.load(myqueue1, myqueue2)) function Queue.load(...) if not ... then return end for _, queue in pairs({...}) do if queue.first then setmetatable(queue, Queue._mt) end end end -- --- Gets the index which matches the stored data -- TODO future direction -- function Queue.get_index(queue, obj) -- for i, v in pairs(queue.objects) do -- if v == obj then -- return i -- end -- end -- end -- TODO pop at index, everything after index insert at -1 dec last -- function Queue.pop_at_index(queue, index) -- return queue.objects[index] -- end -- TODO push at index, everything at and after index + 1 inc last -- function Queue.push_at_index(queue, index) -- return queue -- end --- Push a new element to the front of the queue. -- @param queue (@{Queue}) the queue to push an element to -- @tparam Mixed value the element to push function Queue.push_first(queue, value) Is.Assert(value, 'must have a value to push') local first = queue.first - 1 queue.first = first queue.objects[first] = value return queue end --- Push a new element to the back of the queue. -- @param queue (@{Queue}) the queue to push an element to -- @tparam Mixed value the element to push function Queue.push_last(queue, value) Is.Assert(value, 'must have a value to push') local last = queue.last + 1 queue.last = last queue.objects[last] = value return queue end --- Shortcut for @{Queue.push_last} -- @function Queue.push Queue.push = Queue.push_last --- Retrieve the element at the front of the queue and remove it from the queue. -- @param queue (@{Queue}) the queue to retrieve the element from -- @treturn Mixed value the element at the front of the queue function Queue.pop_first(queue) if Queue.is_empty(queue) then return nil end local first = queue.first local value = queue.objects[first] queue.objects[first] = nil -- to allow garbage collection queue.first = first + 1 return value end --- Shortcut for @{Queue.pop_first} -- @function Queue.pop Queue.pop = Queue.pop_first --- Return the element at the front of the queue and remove it from the queue. -- @param queue (@{Queue}) the queue to retrieve the element from -- @treturn Mixed the element at the front of the queue function Queue.peek_first(queue) return queue.objects[queue.first] end --- Shortcut for @{Queue.peek_first} -- @function Queue.peek Queue.peek = Queue.peek_first --- Retrieve the element at the back of the queue and remove it from the queue. -- @param queue (@{Queue}) the queue to retrieve the element from -- @treturn Mixed the element at the back of the queue function Queue.pop_last(queue) if queue.is_empty(queue) then return nil end local last = queue.last local value = queue.objects[last] queue.objects[last] = nil -- to allow garbage collection queue.last = last - 1 return value end --- Return the element at the back of the queue. -- @param queue (@{Queue}) the queue to retrieve the element from -- @treturn Mixed the element at the back of the queue function Queue.peek_last(queue) return queue.objects[queue.last] end --- Returns true if the given queue is empty. -- @param queue (@{Queue}) the queue to check -- @treturn boolean true if empty, false otherwise function Queue.is_empty(queue) return t_size(queue.objects) == 0 end --- Returns the number of items in the queue. -- @param queue (@{Queue}) the queue to check -- @treturn number the number of items in the queue function Queue.count(queue) return t_size(queue.objects) end function Queue.pairs(queue, pop) local i = queue.first - 1 return function() i = i + 1 local v = pop and queue.pop(queue) or queue.objects[i] if v then return i, v end end end Queue.iter = Queue.pairs Queue.iter_first = Queue.pairs function Queue.rpairs(queue, pop) local i = queue.last + 1 return function() i = i - 1 local v = pop and queue.pop_last(queue) or queue.objects[i] if v then return i, v end end end Queue.iter_last = Queue.rpairs Queue._mt = { __index = __index, __pairs = Queue.pairs, __ipairs = Queue.pairs, __len = Queue.count, __call = __call } return Queue