1+ /**
2+ * Copyright (c) Facebook, Inc. and its affiliates.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ *
7+ * @flow
8+ */
9+
10+ /**
11+ * Modified by Hyeseong Kim <[email protected] > 12+
13+ * Taken this source code from https://github.com/facebook/react/blob/3e94bce765/packages/react-cache/src/LRU.js
14+ * due to Facebook doesn't publish new implementation to NPM.
15+ */
16+ import * as Scheduler from 'scheduler' ; // Intentionally not named imports because Rollup would
17+ // use dynamic dispatch for CommonJS interop named imports.
18+
19+ const {
20+ unstable_scheduleCallback : scheduleCallback ,
21+ unstable_IdlePriority : IdlePriority
22+ } = Scheduler ;
23+ /*:: type Entry<T> = {|
24+ value: T,
25+ onDelete: () => mixed,
26+ previous: Entry<T>,
27+ next: Entry<T>,
28+ |};*/
29+
30+ export function createLRU
31+ /*:: <T>*/
32+ ( limit
33+ /*: number*/
34+ ) {
35+ let LIMIT = limit ; // Circular, doubly-linked list
36+
37+ let first
38+ /*: Entry<T> | null*/
39+ = null ;
40+ let size
41+ /*: number*/
42+ = 0 ;
43+ let cleanUpIsScheduled
44+ /*: boolean*/
45+ = false ;
46+
47+ function scheduleCleanUp ( ) {
48+ if ( cleanUpIsScheduled === false && size > LIMIT ) {
49+ // The cache size exceeds the limit. Schedule a callback to delete the
50+ // least recently used entries.
51+ cleanUpIsScheduled = true ;
52+ scheduleCallback ( IdlePriority , cleanUp ) ;
53+ }
54+ }
55+
56+ function cleanUp ( ) {
57+ cleanUpIsScheduled = false ;
58+ deleteLeastRecentlyUsedEntries ( LIMIT ) ;
59+ }
60+
61+ function deleteLeastRecentlyUsedEntries ( targetSize
62+ /*: number*/
63+ ) {
64+ // Delete entries from the cache, starting from the end of the list.
65+ if ( first !== null ) {
66+ const resolvedFirst
67+ /*: Entry<T>*/
68+ = ( first
69+ /*: any*/
70+ ) ;
71+ let last = resolvedFirst . previous ;
72+
73+ while ( size > targetSize && last !== null ) {
74+ const onDelete = last . onDelete ;
75+ const previous = last . previous ;
76+ last . onDelete = ( null
77+ /*: any*/
78+ ) ; // Remove from the list
79+
80+ last . previous = last . next = ( null
81+ /*: any*/
82+ ) ;
83+
84+ if ( last === first ) {
85+ // Reached the head of the list.
86+ first = last = null ;
87+ } else {
88+ ( first
89+ /*: any*/
90+ ) . previous = previous ;
91+ previous . next = ( first
92+ /*: any*/
93+ ) ;
94+ last = previous ;
95+ }
96+
97+ size -= 1 ; // Call the destroy method after removing the entry from the list. If it
98+ // throws, the rest of cache will not be deleted, but it will be in a
99+ // valid state.
100+
101+ onDelete ( ) ;
102+ }
103+ }
104+ }
105+
106+ function add ( value
107+ /*: Object*/
108+ , onDelete
109+ /*: () => mixed*/
110+ )
111+ /*: Entry<Object>*/
112+ {
113+ const entry = {
114+ value,
115+ onDelete,
116+ next : ( null
117+ /*: any*/
118+ ) ,
119+ previous : ( null
120+ /*: any*/
121+ )
122+ } ;
123+
124+ if ( first === null ) {
125+ entry . previous = entry . next = entry ;
126+ first = entry ;
127+ } else {
128+ // Append to head
129+ const last = first . previous ;
130+ last . next = entry ;
131+ entry . previous = last ;
132+ first . previous = entry ;
133+ entry . next = first ;
134+ first = entry ;
135+ }
136+
137+ size += 1 ;
138+ return entry ;
139+ }
140+
141+ function update ( entry
142+ /*: Entry<T>*/
143+ , newValue
144+ /*: T*/
145+ )
146+ /*: void*/
147+ {
148+ entry . value = newValue ;
149+ }
150+
151+ function access ( entry
152+ /*: Entry<T>*/
153+ )
154+ /*: T*/
155+ {
156+ const next = entry . next ;
157+
158+ if ( next !== null ) {
159+ // Entry already cached
160+ const resolvedFirst
161+ /*: Entry<T>*/
162+ = ( first
163+ /*: any*/
164+ ) ;
165+
166+ if ( first !== entry ) {
167+ // Remove from current position
168+ const previous = entry . previous ;
169+ previous . next = next ;
170+ next . previous = previous ; // Append to head
171+
172+ const last = resolvedFirst . previous ;
173+ last . next = entry ;
174+ entry . previous = last ;
175+ resolvedFirst . previous = entry ;
176+ entry . next = resolvedFirst ;
177+ first = entry ;
178+ }
179+ } else { // Cannot access a deleted entry
180+ // TODO: Error? Warning?
181+ }
182+
183+ scheduleCleanUp ( ) ;
184+ return entry . value ;
185+ }
186+
187+ function setLimit ( newLimit
188+ /*: number*/
189+ ) {
190+ LIMIT = newLimit ;
191+ scheduleCleanUp ( ) ;
192+ }
193+
194+ return {
195+ add,
196+ update,
197+ access,
198+ setLimit
199+ } ;
200+ }
0 commit comments