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