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 <ae@cy.md> 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 // https://issues.dlang.org/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 private 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 }