--- 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 Misc.Queue -- @usage local Queue = require('__stdlib__/stdlib/misc/queue') -- local q = Queue() -- create a new empty queue -- q('my value') -- push a value onto the queue -- q() -- pop the last value off the queue -- game.print(#q) -- print the number of items in the queue local Queue = { __class = 'Queue', __index = require('__stdlib__/stdlib/core') } setmetatable(Queue, Queue) local table = require('__stdlib__/stdlib/utils/table') local t_size = table_size local Inspect = require('__stdlib__/stdlib/vendor/inspect') local meta = {} function Queue.__call(_, ...) local queue = { first = 1, last = 0, objects = {} } setmetatable(queue, meta) for _, push in pairs { ... } do queue(push) end return queue end --- Constructs a new Queue object. -- @param ... mixed, values to push into the queue -- @treturn @{Queue} a new queue function Queue.new(...) return Queue.__call(nil, ...) 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.
-- @tparam table queue (@{Queue},...)
-- @usage global.myqueue1 = Queue.new()
-- script.on_load(function() Queue.load(global.myqueue))
function Queue.load(queue)
if type(queue) == 'table' and queue.first then
return setmetatable(queue, meta)
end
end
--- Push a new element to the front of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam Mixed value the element to push
function Queue.push_first(queue, ...)
for _, value in pairs { ... } do
queue.first = queue.first - 1
queue.objects[queue.first] = value
end
return queue
end
--- Push a new element to the back of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam Mixed ... the element(s) to push
function Queue.push_last(queue, ...)
for _, value in pairs { ... } do
queue.last = queue.last + 1
queue.objects[queue.last] = value
end
return queue
end
--- Shortcut for @{Queue.push_last}
-- @function Queue.push
Queue.push = Queue.push_last
--- Push a new element to a specific location of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam number index the index to push to.
-- @tparam Mixed value the element to push.
function Queue.push_at(queue, index, value)
if index < queue.first then
return Queue.push_first(queue, value)
elseif index > queue.last then
return Queue.push_last(queue, value)
else
table.insert(queue.objects, index, value)
queue.last = queue.last + 1
end
return queue
end
function Queue.wrapper(self, func_name, ...)
if Queue[func_name] then
Queue[func_name](self, ...)
end
return self
end
--- Retrieve the element at the front of the queue and remove it from the queue.
-- @tparam 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
local function remove(queue, index)
local ret = queue.objects[index]
if ret ~= nil then
for i = index + 1, queue.last do
queue.objects[i - 1] = queue.objects[i]
end
queue.objects[queue.last] = nil
queue.last = queue.last - 1
end
return ret
end
--- Pop an element at a specific location of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam number index the index to push to.
-- @treturn Mixed value the popped element.
function Queue.pop_at(queue, index)
return remove(queue, index)
end
--- Peek at an element in the queue without disturbing the queue.
-- @tparam Queue queue the queue to peek at
-- @tparam number index the index in the queue to peek at
-- @treturn Mixed the value of the peeked element
function Queue.peek_at(queue, index)
return queue.objects[index]
end
--- Return the element at the front of the queue and remove it from the queue.
-- @tparam 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.
-- @tparam 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.
-- @tparam 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 the popped value and pushes back into the queue.
-- @tparam Queue queue the queue
-- @return Mixed the value that was popped.
function Queue.pop_and_push(queue)
local ret = queue.pop(queue)
queue.push(queue, ret)
return ret
end
--- Returns the queue after popping the last element and pushing it to the top.
-- @tparam Queue queue the queue
-- @treturn @{Queue} the queue
function Queue.cycle(queue)
return queue.push(queue, queue.pop(queue))
end
--- Gets the first index which matches the stored data. does not compare inside tables.
function Queue.find(queue, find)
for i, v in pairs(queue) do
if v == find then
return i
end
end
end
local function _sort_func(a, b)
local lhs = type(a) == 'table' and '{' or tostring(a)
local rhs = type(b) == 'table' and '{' or tostring(b)
return lhs < rhs
end
--- sort and reorder the queue
function Queue.sort(queue, func)
local sorted = {}
for _, v in pairs(queue) do
if v ~= nil then
sorted[#sorted + 1] = v
end
end
table.sort(sorted, func or _sort_func)
queue.objects = sorted
queue.first, queue.last = 1, #queue.objects
return queue
end
--- Returns true if the given queue is empty.
-- @tparam Queue queue the queue to check
-- @treturn boolean true if empty, false otherwise
function Queue.is_empty(queue)
return queue.first > queue.last
end
--- Returns the number of items in the queue.
-- @tparam Queue queue the queue to check
-- @treturn number the number of items in the queue
function Queue.size(queue)
return t_size(queue.objects)
end
--- Shortcut for @{Queue.size}
-- @function Queue.count
Queue.count = Queue.size
--- Return the next element in the queue
-- @tparam Queue queue the queue to check
-- @tparam number|nil index if nil return the first value, else return the next index value
-- @tparam boolean pop pop the value off the queue
-- @treturn number|nil the index
-- @treturn Mixed|nil the value at queue index
function Queue.next(queue, index, pop)
index = not index and queue.first or index + (pop and 0 or 1)
for i = index, queue.last do
local v = queue.objects[i]
if v ~= nil then
return i, pop and Queue.pop_at(queue, i) or v
end
end
return nil, nil
end
--- Return the previous element in the queue
-- @tparam Queue queue the queue to check
-- @tparam number|nil index if nil return the last value, else return the previous index value
-- @tparam boolean pop pop the value off the queue
-- @treturn number|nil the index
-- @treturn Mixed|nil the value at queue index
function Queue.rnext(queue, index, pop)
-- next returns index of next or nil and data,
index = not index and queue.last or (index < queue.first and queue.first or index) - 1
for i = index, queue.first, -1 do
local v = queue.objects[i]
if v ~= nil then
return i, pop and Queue.pop_at(queue, i) or v
end
end
return nil, nil
end
local function next_pop(queue, index)
return Queue.next(queue, index, true)
end
local function rnext_pop(queue, index)
return Queue.rnext(queue, index, true)
end
--- Iterate the queue forward
function Queue.pairs(queue, pop)
return pop and next_pop or Queue.next, queue, nil
end
--- Iterate the queue backwards
function Queue.rpairs(queue, pop)
return pop and rnext_pop or Queue.rnext, queue, nil
end
do
meta.__class = 'queue'
meta.__len = Queue.size
meta.__unm = Queue.pop
meta.__parent = Queue
meta.__debugline = [[