Merge pull request #352 from psychon/gears_cache
Add gears.cache: a generic cache which may loose values at any time
This commit is contained in:
commit
c602eb4ff7
|
@ -0,0 +1,52 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- @author Uli Schlachter
|
||||||
|
-- @copyright 2015 Uli Schlachter
|
||||||
|
-- @release @AWESOME_VERSION@
|
||||||
|
-- @classmod gears.cache
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local select = select
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local unpack = unpack or table.unpack
|
||||||
|
|
||||||
|
local cache = {}
|
||||||
|
|
||||||
|
--- Get an entry from the cache, creating it if it's missing.
|
||||||
|
-- @param ... Arguments for the creation callback. These are checked against the
|
||||||
|
-- cache contents for equality.
|
||||||
|
-- @return The entry from the cache
|
||||||
|
function cache:get(...)
|
||||||
|
local result = self._cache
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
local arg = select(i, ...)
|
||||||
|
local next = result[arg]
|
||||||
|
if not next then
|
||||||
|
next = {}
|
||||||
|
result[arg] = next
|
||||||
|
end
|
||||||
|
result = next
|
||||||
|
end
|
||||||
|
local ret = result._entry
|
||||||
|
if not ret then
|
||||||
|
ret = { self._creation_cb(...) }
|
||||||
|
result._entry = ret
|
||||||
|
end
|
||||||
|
return unpack(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new cache object. A cache keeps some data that can be
|
||||||
|
-- garbage-collected at any time, but might be useful to keep.
|
||||||
|
-- @param creation_cb Callback that is used for creating missing cache entries.
|
||||||
|
-- @return A new cache object.
|
||||||
|
function cache.new(creation_cb)
|
||||||
|
return setmetatable({
|
||||||
|
_cache = setmetatable({}, { __mode = "v" }),
|
||||||
|
_creation_cb = creation_cb
|
||||||
|
}, {
|
||||||
|
__index = cache
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(cache, { __call = function(_, ...) return cache.new(...) end })
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -17,7 +17,7 @@ local cairo = require("lgi").cairo
|
||||||
local surface = require("gears.surface")
|
local surface = require("gears.surface")
|
||||||
|
|
||||||
local color = { mt = {} }
|
local color = { mt = {} }
|
||||||
local pattern_cache = setmetatable({}, { __mode = 'v' })
|
local pattern_cache
|
||||||
|
|
||||||
--- Parse a HTML-color.
|
--- Parse a HTML-color.
|
||||||
-- This function can parse colors like `#rrggbb` and `#rrggbbaa`.
|
-- This function can parse colors like `#rrggbb` and `#rrggbbaa`.
|
||||||
|
@ -231,13 +231,7 @@ function color.create_pattern(col)
|
||||||
if cairo.Pattern:is_type_of(col) then
|
if cairo.Pattern:is_type_of(col) then
|
||||||
return col
|
return col
|
||||||
end
|
end
|
||||||
local col = col or "#000000"
|
return pattern_cache:get(col or "#000000")
|
||||||
local result = pattern_cache[col]
|
|
||||||
if not result then
|
|
||||||
result = color.create_pattern_uncached(col)
|
|
||||||
pattern_cache[col] = result
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if a pattern is opaque.
|
--- Check if a pattern is opaque.
|
||||||
|
@ -296,6 +290,8 @@ function color.mt:__call(...)
|
||||||
return color.create_pattern(...)
|
return color.create_pattern(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pattern_cache = require("gears.cache").new(color.create_pattern_uncached)
|
||||||
|
|
||||||
return setmetatable(color, color.mt)
|
return setmetatable(color, color.mt)
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -15,6 +15,7 @@ return
|
||||||
surface = require("gears.surface");
|
surface = require("gears.surface");
|
||||||
wallpaper = require("gears.wallpaper");
|
wallpaper = require("gears.wallpaper");
|
||||||
timer = require("gears.timer");
|
timer = require("gears.timer");
|
||||||
|
cache = require("gears.cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -38,21 +38,7 @@ function base.fit_widget(widget, width, height)
|
||||||
local width = math.max(0, width)
|
local width = math.max(0, width)
|
||||||
local height = math.max(0, height)
|
local height = math.max(0, height)
|
||||||
|
|
||||||
-- Since the geometry cache is a weak table, we have to be careful when
|
return widget._fit_geometry_cache:get(width, height)
|
||||||
-- doing lookups. We can't do "if cache[width] ~= nil then"!
|
|
||||||
local cache = widget._fit_geometry_cache
|
|
||||||
local result = cache[width]
|
|
||||||
if not result then
|
|
||||||
result = {}
|
|
||||||
cache[width] = result
|
|
||||||
end
|
|
||||||
cache, result = result, result[height]
|
|
||||||
if not result then
|
|
||||||
local w, h = widget:fit(width, height)
|
|
||||||
result = { width = w, height = h }
|
|
||||||
cache[height] = result
|
|
||||||
end
|
|
||||||
return result.width, result.height
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Draw a widget via a cairo context
|
--- Draw a widget via a cairo context
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
local debug = require("gears.debug")
|
local debug = require("gears.debug")
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
|
local cache = require("gears.cache")
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
local type = type
|
local type = type
|
||||||
|
@ -98,9 +99,12 @@ function base.make_widget(proxy, widget_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add a geometry for base.fit_widget() that is cleared when necessary
|
-- Add a geometry for base.fit_widget() that is cleared when necessary
|
||||||
ret._fit_geometry_cache = setmetatable({}, { __mode = 'v' })
|
local function cb(...)
|
||||||
|
return ret:fit(...)
|
||||||
|
end
|
||||||
|
ret._fit_geometry_cache = cache.new(cb)
|
||||||
ret:connect_signal("widget::updated", function()
|
ret:connect_signal("widget::updated", function()
|
||||||
ret._fit_geometry_cache = setmetatable({}, { __mode = 'v' })
|
ret._fit_geometry_cache = cache.new(cb)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Add __tostring method to metatable.
|
-- Add __tostring method to metatable.
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- @author Uli Schlachter
|
||||||
|
-- @copyright 2015 Uli Schlachter
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local cache = require("gears.cache")
|
||||||
|
|
||||||
|
describe("gears.cache", function()
|
||||||
|
-- Make sure no cache is cleared during the tests
|
||||||
|
before_each(function()
|
||||||
|
collectgarbage("stop")
|
||||||
|
end)
|
||||||
|
after_each(function()
|
||||||
|
collectgarbage("restart")
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Zero arguments", function()
|
||||||
|
it("Creation cb is called", function()
|
||||||
|
local called = false
|
||||||
|
local c = cache(function()
|
||||||
|
called = true
|
||||||
|
end)
|
||||||
|
local res = c:get()
|
||||||
|
assert.is_nil(res)
|
||||||
|
assert.is_true(called)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Two arguments", function()
|
||||||
|
it("Cache works", function()
|
||||||
|
local num_calls = 0
|
||||||
|
local c = cache(function(a, b)
|
||||||
|
num_calls = num_calls + 1
|
||||||
|
return a + b
|
||||||
|
end)
|
||||||
|
local res1 = c:get(1, 2)
|
||||||
|
local res2 = c:get(1, 3)
|
||||||
|
local res3 = c:get(1, 2)
|
||||||
|
assert.is.equal(res1, 3)
|
||||||
|
assert.is.equal(res2, 4)
|
||||||
|
assert.is.equal(res3, 3)
|
||||||
|
assert.is.equal(num_calls, 2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Cache invalidation works", function()
|
||||||
|
local num_calls = 0
|
||||||
|
local c = cache(function(a, b)
|
||||||
|
num_calls = num_calls + 1
|
||||||
|
return a + b
|
||||||
|
end)
|
||||||
|
local res1 = c:get(1, 2)
|
||||||
|
collectgarbage("collect")
|
||||||
|
local res2 = c:get(1, 2)
|
||||||
|
assert.is.equal(res1, 3)
|
||||||
|
assert.is.equal(res2, 3)
|
||||||
|
assert.is.equal(num_calls, 2)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -4,6 +4,7 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
|
local cache = require("gears.cache")
|
||||||
local wbase = require("wibox.widget.base")
|
local wbase = require("wibox.widget.base")
|
||||||
local lbase = require("wibox.layout.base")
|
local lbase = require("wibox.layout.base")
|
||||||
local say = require("say")
|
local say = require("say")
|
||||||
|
@ -64,7 +65,7 @@ return {
|
||||||
return width or 10, height or 10
|
return width or 10, height or 10
|
||||||
end
|
end
|
||||||
w.draw = function() end
|
w.draw = function() end
|
||||||
w._fit_geometry_cache = {}
|
w._fit_geometry_cache = cache.new(w.fit)
|
||||||
|
|
||||||
spy.on(w, "fit")
|
spy.on(w, "fit")
|
||||||
stub(w, "draw")
|
stub(w, "draw")
|
||||||
|
|
Loading…
Reference in New Issue