Skip to content

Commit 0919997

Browse files
committed
add ByteArrayCache and IntArrayCache
1 parent 21f460f commit 0919997

File tree

7 files changed

+479
-30
lines changed

7 files changed

+479
-30
lines changed

net.lecousin.core/src/main/java/net/lecousin/framework/collections/sort/RedBlackTreeInteger.java

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -272,34 +272,69 @@ private boolean containsInstance(Node<T> x, int key, T instance) {
272272
/**
273273
* Returns the node containing the highest value strictly lower than the given one.
274274
*/
275-
public Node<T> searchNearestLower(int value) {
275+
public Node<T> searchNearestLower(int value, boolean acceptEquals) {
276276
if (root == null) return null;
277-
return searchNearestLower(root, value);
277+
return searchNearestLower(root, value, acceptEquals);
278278
}
279279

280-
private Node<T> searchNearestLower(Node<T> node, int value) {
280+
private Node<T> searchNearestLower(Node<T> node, int value, boolean acceptEquals) {
281281
if (value < node.value) {
282282
if (node.left == null) return null;
283-
return searchNearestLower(node.left, value);
283+
return searchNearestLower(node.left, value, acceptEquals);
284284
}
285285
if (value > node.value) {
286286
if (node.right == null) return node;
287-
if (value > node.right.value) return searchNearestLower(node.right, value);
287+
if (value > node.right.value) return searchNearestLower(node.right, value, acceptEquals);
288288
if (value == node.right.value) {
289-
Node<T> n = searchNearestLower(node.right, value);
289+
if (acceptEquals) return node.right;
290+
Node<T> n = searchNearestLower(node.right, value, acceptEquals);
290291
if (n == null) return node;
291292
return n;
292293
}
293-
Node<T> n = searchNearestLower(node.right, value);
294+
Node<T> n = searchNearestLower(node.right, value, acceptEquals);
294295
if (n == null) return node;
295296
return n;
296297
}
298+
if (acceptEquals) return node;
297299
if (node.left == null) return null;
298300
Node<T> n = node.left;
299301
while (n.right != null) n = n.right;
300302
return n;
301303
}
302304

305+
/**
306+
* Returns the node containing the lowest value strictly higher than the given one.
307+
*/
308+
public Node<T> searchNearestHigher(int value, boolean acceptEquals) {
309+
if (root == null) return null;
310+
return searchNearestHigher(root, value, acceptEquals);
311+
}
312+
313+
private Node<T> searchNearestHigher(Node<T> node, int value, boolean acceptEquals) {
314+
if (value > node.value) {
315+
if (node.right == null) return null;
316+
return searchNearestHigher(node.right, value, acceptEquals);
317+
}
318+
if (value < node.value) {
319+
if (node.left == null) return node;
320+
if (value < node.left.value) return searchNearestHigher(node.left, value, acceptEquals);
321+
if (value == node.left.value) {
322+
if (acceptEquals) return node.left;
323+
Node<T> n = searchNearestHigher(node.left, value, acceptEquals);
324+
if (n == null) return node;
325+
return n;
326+
}
327+
Node<T> n = searchNearestHigher(node.left, value, acceptEquals);
328+
if (n == null) return node;
329+
return n;
330+
}
331+
if (acceptEquals) return node;
332+
if (node.right == null) return null;
333+
Node<T> n = node.right;
334+
while (n.left != null) n = n.left;
335+
return n;
336+
}
337+
303338
// -----------------
304339
// --- insertion ---
305340
// -----------------
@@ -667,6 +702,8 @@ private Node<T> min(Node<T> x) {
667702

668703
@Override
669704
public Iterator<T> iterator() { return new RBTreeIterator(root); }
705+
706+
public Iterator<Node<T>> nodeIterator() { return new RBTreeNodeIterator(root); }
670707

671708
public Iterator<Node<T>> nodeIteratorOrdered() { return new NodeIteratorOrdered<T>(root); }
672709

@@ -713,6 +750,41 @@ public T next() {
713750
}
714751
}
715752

753+
private class RBTreeNodeIterator implements Iterator<Node<T>> {
754+
private RBTreeNodeIterator(Node<T> node) {
755+
this.node = node;
756+
}
757+
758+
private Node<T> node;
759+
private RBTreeNodeIterator leftIterator = null;
760+
private RBTreeNodeIterator rightIterator = null;
761+
762+
@Override
763+
public boolean hasNext() {
764+
if (node != null || leftIterator != null || rightIterator != null) return true;
765+
return false;
766+
}
767+
768+
@Override
769+
public Node<T> next() {
770+
if (node != null) {
771+
if (node.left != null) leftIterator = new RBTreeNodeIterator(node.left);
772+
if (node.right != null) rightIterator = new RBTreeNodeIterator(node.right);
773+
Node<T> n = node;
774+
node = null;
775+
return n;
776+
}
777+
if (leftIterator != null) {
778+
Node<T> n = leftIterator.next();
779+
if (!leftIterator.hasNext()) leftIterator = null;
780+
return n;
781+
}
782+
Node<T> n = rightIterator.next();
783+
if (!rightIterator.hasNext()) rightIterator = null;
784+
return n;
785+
}
786+
}
787+
716788
private static class NodeIteratorOrdered<T> implements Iterator<Node<T>> {
717789
private NodeIteratorOrdered(Node<T> root) {
718790
next = root;

net.lecousin.core/src/main/java/net/lecousin/framework/collections/sort/RedBlackTreeLong.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,39 @@ private Node<T> searchNearestLower(Node<T> node, long value, boolean acceptEqual
304304
return n;
305305
}
306306

307+
/**
308+
* Returns the node containing the lowest value strictly higher than the given one.
309+
*/
310+
public Node<T> searchNearestHigher(long value, boolean acceptEquals) {
311+
if (root == null) return null;
312+
return searchNearestHigher(root, value, acceptEquals);
313+
}
314+
315+
private Node<T> searchNearestHigher(Node<T> node, long value, boolean acceptEquals) {
316+
if (value > node.value) {
317+
if (node.right == null) return null;
318+
return searchNearestHigher(node.right, value, acceptEquals);
319+
}
320+
if (value < node.value) {
321+
if (node.left == null) return node;
322+
if (value < node.left.value) return searchNearestHigher(node.left, value, acceptEquals);
323+
if (value == node.left.value) {
324+
if (acceptEquals) return node.left;
325+
Node<T> n = searchNearestHigher(node.left, value, acceptEquals);
326+
if (n == null) return node;
327+
return n;
328+
}
329+
Node<T> n = searchNearestHigher(node.left, value, acceptEquals);
330+
if (n == null) return node;
331+
return n;
332+
}
333+
if (acceptEquals) return node;
334+
if (node.right == null) return null;
335+
Node<T> n = node.right;
336+
while (n.left != null) n = n.left;
337+
return n;
338+
}
339+
307340
// -----------------
308341
// --- insertion ---
309342
// -----------------
@@ -671,6 +704,8 @@ private Node<T> min(Node<T> x) {
671704
@Override
672705
public Iterator<T> iterator() { return new RBTreeIterator(root); }
673706

707+
public Iterator<Node<T>> nodeIterator() { return new RBTreeNodeIterator(root); }
708+
674709
public Iterator<Node<T>> nodeIteratorOrdered() { return new NodeIteratorOrdered<T>(root); }
675710

676711
public Iterator<Node<T>> nodeIteratorReverse() { return new NodeIteratorReverseOrder<T>(root); }
@@ -716,6 +751,41 @@ public T next() {
716751
}
717752
}
718753

754+
private class RBTreeNodeIterator implements Iterator<Node<T>> {
755+
private RBTreeNodeIterator(Node<T> node) {
756+
this.node = node;
757+
}
758+
759+
private Node<T> node;
760+
private RBTreeNodeIterator leftIterator = null;
761+
private RBTreeNodeIterator rightIterator = null;
762+
763+
@Override
764+
public boolean hasNext() {
765+
if (node != null || leftIterator != null || rightIterator != null) return true;
766+
return false;
767+
}
768+
769+
@Override
770+
public Node<T> next() {
771+
if (node != null) {
772+
if (node.left != null) leftIterator = new RBTreeNodeIterator(node.left);
773+
if (node.right != null) rightIterator = new RBTreeNodeIterator(node.right);
774+
Node<T> n = node;
775+
node = null;
776+
return n;
777+
}
778+
if (leftIterator != null) {
779+
Node<T> n = leftIterator.next();
780+
if (!leftIterator.hasNext()) leftIterator = null;
781+
return n;
782+
}
783+
Node<T> n = rightIterator.next();
784+
if (!rightIterator.hasNext()) rightIterator = null;
785+
return n;
786+
}
787+
}
788+
719789
private static class NodeIteratorOrdered<T> implements Iterator<Node<T>> {
720790
private NodeIteratorOrdered(Node<T> root) {
721791
next = root;

net.lecousin.core/src/main/java/net/lecousin/framework/io/IOUtil.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1322,7 +1322,16 @@ void success(TResult res, AsyncWork<TResult, TError> result, RunnableWithParamet
13221322
AsyncWork<TResult, TError> success(TResult result, RunnableWithParameter<Pair<TResult, TError>> ondone) {
13231323
if (ondone != null) ondone.run(new Pair<>(result, null));
13241324
return new AsyncWork<>(result, null);
1325-
}
1325+
}
1326+
1327+
/** Shortcut to transfer error or cancellation. */
1328+
public static <TResult, TError extends Exception>
1329+
void notSuccess(ISynchronizationPoint<TError> sp, AsyncWork<TResult, TError> result, RunnableWithParameter<Pair<TResult, TError>> ondone) {
1330+
if (sp.hasError())
1331+
error(sp.getError(), result, ondone);
1332+
else
1333+
result.cancel(sp.getCancelEvent());
1334+
}
13261335

13271336
/** Get the size by seeking at the end if not an instanceof IO.KnownSize. */
13281337
public static long getSizeSync(IO.Readable.Seekable io) throws IOException {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package net.lecousin.framework.memory;
2+
3+
import java.util.Arrays;
4+
import java.util.Iterator;
5+
import java.util.LinkedList;
6+
import java.util.List;
7+
8+
import net.lecousin.framework.application.Application;
9+
import net.lecousin.framework.application.LCCore;
10+
import net.lecousin.framework.collections.TurnArray;
11+
import net.lecousin.framework.collections.sort.RedBlackTreeInteger;
12+
import net.lecousin.framework.collections.sort.RedBlackTreeInteger.Node;
13+
14+
public class ByteArrayCache implements IMemoryManageable {
15+
16+
public static ByteArrayCache getInstance() {
17+
Application app = LCCore.getApplication();
18+
synchronized (ByteArrayCache.class) {
19+
ByteArrayCache instance = app.getInstance(ByteArrayCache.class);
20+
if (instance != null)
21+
return instance;
22+
app.setInstance(ByteArrayCache.class, instance = new ByteArrayCache());
23+
return instance;
24+
}
25+
}
26+
27+
public int MAX_BUFFERS_BY_SIZE_UNDER_128KB = 20;
28+
public int MAX_BUFFERS_BY_SIZE_ABOVE_128KB = 8;
29+
public int MAX_TOTAL_SIZE = 64 * 1024 * 1024;
30+
public long TIME_BEFORE_TO_REMOVE_ONE = 5 * 60 * 1000; // every 5 minutes
31+
32+
private static class ArraysBySize {
33+
private TurnArray<byte[]> arrays;
34+
private long lastCachedTime;
35+
private long lastUsageTime;
36+
}
37+
38+
private RedBlackTreeInteger<ArraysBySize> arraysBySize = new RedBlackTreeInteger<>();
39+
private int totalSize = 0;
40+
41+
private ByteArrayCache() {
42+
// TODO background task in addition to the memory manager ?
43+
MemoryManager.register(this);
44+
}
45+
46+
public byte[] get(int size, boolean acceptGreater) {
47+
Node<ArraysBySize> node;
48+
byte[] buf;
49+
synchronized (arraysBySize) {
50+
if (acceptGreater)
51+
node = arraysBySize.searchNearestHigher(size, true);
52+
else
53+
node = arraysBySize.get(size);
54+
// limit to 3/2 of the size
55+
if (node != null && acceptGreater && node.getValue() > size * 3 / 2)
56+
node = null;
57+
if (node == null)
58+
buf = null;
59+
else {
60+
ArraysBySize arrays = node.getElement();
61+
arrays.lastUsageTime = System.currentTimeMillis();
62+
buf = arrays.arrays.poll();
63+
}
64+
}
65+
if (buf == null)
66+
return new byte[size];
67+
totalSize -= buf.length;
68+
return buf;
69+
}
70+
71+
public void free(byte[] array) {
72+
int len = array.length;
73+
synchronized (arraysBySize) {
74+
if (totalSize + len > MAX_TOTAL_SIZE) return;
75+
Node<ArraysBySize> node = arraysBySize.get(len);
76+
if (node == null) {
77+
ArraysBySize arrays = new ArraysBySize();
78+
arrays.arrays = new TurnArray<>(
79+
len >= 128 * 1024 ? MAX_BUFFERS_BY_SIZE_ABOVE_128KB : MAX_BUFFERS_BY_SIZE_UNDER_128KB);
80+
arrays.arrays.add(array);
81+
arrays.lastCachedTime = System.currentTimeMillis();
82+
arraysBySize.add(len, arrays);
83+
totalSize += len;
84+
return;
85+
}
86+
ArraysBySize arrays = node.getElement();
87+
arrays.lastCachedTime = System.currentTimeMillis();
88+
if (arrays.arrays.isFull()) return;
89+
totalSize += len;
90+
arrays.arrays.add(array);
91+
}
92+
}
93+
94+
@Override
95+
public String getDescription() {
96+
return "Byte arrays cache";
97+
}
98+
99+
@Override
100+
public List<String> getItemsDescription() {
101+
return Arrays.asList("Total cached size: " + totalSize);
102+
}
103+
104+
@Override
105+
public void freeMemory(FreeMemoryLevel level) {
106+
long now = System.currentTimeMillis();
107+
switch (level) {
108+
default:
109+
case EXPIRED_ONLY:
110+
freeMemory(now - TIME_BEFORE_TO_REMOVE_ONE, 1);
111+
break;
112+
case LOW:
113+
freeMemory(now - TIME_BEFORE_TO_REMOVE_ONE / 2, 2);
114+
break;
115+
case MEDIUM:
116+
freeMemory(now - TIME_BEFORE_TO_REMOVE_ONE / 3, 5);
117+
break;
118+
case URGENT:
119+
synchronized (arraysBySize) {
120+
arraysBySize.clear();
121+
totalSize = 0;
122+
}
123+
break;
124+
}
125+
}
126+
127+
private void freeMemory(long beforeTime, int nbToRemove) {
128+
List<Node<ArraysBySize>> toRemove = new LinkedList<>();
129+
synchronized (arraysBySize) {
130+
for (Iterator<Node<ArraysBySize>> it = arraysBySize.nodeIterator(); it.hasNext(); ) {
131+
Node<ArraysBySize> node = it.next();
132+
ArraysBySize arrays = node.getElement();
133+
if (arrays.lastUsageTime < beforeTime) {
134+
if (arrays.lastCachedTime > beforeTime && arrays.arrays.size() <= 2)
135+
continue;
136+
for (int i = 0; i < nbToRemove && !arrays.arrays.isEmpty(); ++i) {
137+
arrays.arrays.poll();
138+
totalSize -= node.getValue();
139+
}
140+
if (arrays.arrays.isEmpty())
141+
toRemove.add(node);
142+
}
143+
}
144+
for (Node<ArraysBySize> node : toRemove)
145+
arraysBySize.remove(node);
146+
}
147+
}
148+
149+
}

0 commit comments

Comments
 (0)