• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Fast QR Code generator library
3  *
4  * Copyright (c) Project Nayuki. (MIT License)
5  * https://www.nayuki.io/page/fast-qr-code-generator-library
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  * - The above copyright notice and this permission notice shall be included in
14  *   all copies or substantial portions of the Software.
15  * - The Software is provided "as is", without warranty of any kind, express or
16  *   implied, including but not limited to the warranties of merchantability,
17  *   fitness for a particular purpose and noninfringement. In no event shall the
18  *   authors or copyright holders be liable for any claim, damages or other
19  *   liability, whether in an action of contract, tort or otherwise, arising from,
20  *   out of or in connection with the Software or the use or other dealings in the
21  *   Software.
22  */
23 
24 package io.nayuki.fastqrcodegen;
25 
26 import java.lang.ref.SoftReference;
27 import java.util.HashSet;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.function.Function;
32 
33 
34 // A thread-safe cache based on soft references.
35 final class Memoizer<T,R> {
36 
37 	private final Function<T,R> function;
38 	Map<T,SoftReference<R>> cache = new ConcurrentHashMap<>();
39 	private Set<T> pending = new HashSet<>();
40 
41 
42 	// Creates a memoizer based on the given function that takes one input to compute an output.
Memoizer(Function<T,R> func)43 	public Memoizer(Function<T,R> func) {
44 		function = func;
45 	}
46 
47 
48 	// Computes function.apply(arg) or returns a cached copy of a previous call.
get(T arg)49 	public R get(T arg) {
50 		// Non-blocking fast path
51 		{
52 			SoftReference<R> ref = cache.get(arg);
53 			if (ref != null) {
54 				R result = ref.get();
55 				if (result != null)
56 					return result;
57 			}
58 		}
59 
60 		// Sequential slow path
61 		while (true) {
62 			synchronized(this) {
63 				SoftReference<R> ref = cache.get(arg);
64 				if (ref != null) {
65 					R result = ref.get();
66 					if (result != null)
67 						return result;
68 					cache.remove(arg);
69 				}
70 				assert !cache.containsKey(arg);
71 
72 				if (pending.add(arg))
73 					break;
74 
75 				try {
76 					this.wait();
77 				} catch (InterruptedException e) {
78 					throw new RuntimeException(e);
79 				}
80 			}
81 		}
82 
83 		try {
84 			R result = function.apply(arg);
85 			cache.put(arg, new SoftReference<>(result));
86 			return result;
87 		} finally {
88 			synchronized(this) {
89 				pending.remove(arg);
90 				this.notifyAll();
91 			}
92 		}
93 	}
94 
95 }
96