1 /**
2  * ae.sys.persistence.memoize
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.sys.persistence.memoize;
15 
16 import std.traits;
17 import std.typecons;
18 
19 import ae.sys.persistence.core;
20 import ae.sys.persistence.json;
21 import ae.utils.json;
22 
23 // ****************************************************************************
24 
25 // http://d.puremagic.com/issues/show_bug.cgi?id=7016
26 static import ae.utils.json;
27 
28 /// std.functional.memoize variant with automatic persistence
29 struct PersistentMemoized(alias fun, FlushPolicy flushPolicy = FlushPolicy.atThreadExit)
30 {
31 	alias AA = ReturnType!fun[string];
32 	private JsonFileCache!(AA, flushPolicy) memo;
33 
34 	this(string fileName) { memo.fileName = fileName; }
35 
36 	ReturnType!fun opCall(ParameterTypeTuple!fun args)
37 	{
38 		string key;
39 		static if (args.length==1 && is(typeof(args[0]) : string))
40 			key = args[0];
41 		else
42 			key = toJson(tuple(args));
43 		auto p = key in memo;
44 		if (p) return *p;
45 		auto r = fun(args);
46 		return memo[key] = r;
47 	}
48 }
49 
50 unittest
51 {
52 	import std.file : exists, remove;
53 
54 	static int value = 42;
55 	int getValue(int x) { return value; }
56 
57 	enum FN = "test4.json";
58 	scope(exit) if (FN.exists) remove(FN);
59 
60 	{
61 		auto getValueMemoized = PersistentMemoized!(getValue, FlushPolicy.atScopeExit)(FN);
62 
63 		assert(getValueMemoized(1) == 42);
64 		value = 24;
65 		assert(getValueMemoized(1) == 42);
66 		assert(getValueMemoized(2) == 24);
67 	}
68 
69 	value = 0;
70 
71 	{
72 		auto getValueMemoized = PersistentMemoized!(getValue, FlushPolicy.atScopeExit)(FN);
73 		assert(getValueMemoized(1) == 42);
74 		assert(getValueMemoized(2) == 24);
75 	}
76 }
77 
78 /// As above, but with synchronization
79 struct SynchronizedPersistentMemoized(alias fun, FlushPolicy flushPolicy = FlushPolicy.atThreadExit)
80 {
81 	alias AA = ReturnType!fun[string];
82 	private JsonFileCache!(AA, flushPolicy) memo;
83 	Object mutex;
84 
85 	this(string fileName)
86 	{
87 		memo.fileName = fileName;
88 		mutex = new Object;
89 	}
90 
91 	ReturnType!fun opCall(ParameterTypeTuple!fun args)
92 	{
93 		string key;
94 		static if (args.length==1 && is(typeof(args[0]) : string))
95 			key = args[0];
96 		else
97 			key = toJson(tuple(args));
98 		synchronized (mutex)
99 		{
100 			auto p = key in memo;
101 			if (p) return *p;
102 		}
103 		auto r = fun(args);
104 		synchronized (mutex)
105 			return memo[key] = r;
106 	}
107 }