perf(mainhttp): micro-optimize internal data structures
This commit is contained in:
parent
243dfcaed0
commit
9e59851a9f
@ -31,7 +31,7 @@ public class HttpDecoder extends ChannelInboundHandlerAdapter {
|
||||
while (buf.isReadable()
|
||||
&& current != null
|
||||
&& current.content == null)
|
||||
current = current.next.get((char) buf.readByte());
|
||||
current = current.next.get(buf.readByte());
|
||||
if (current == null || current.content == null) return;
|
||||
// Method identified, this is HTTP!
|
||||
passOn = false;
|
||||
|
@ -1,37 +1,68 @@
|
||||
package io.gitlab.jfronny.libjf.mainhttp.impl.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ClaimPool<T> {
|
||||
private final List<Claim> content = new LinkedList<>();
|
||||
private Claim top = null;
|
||||
|
||||
public Claim claim(T value) {
|
||||
return new Claim(value);
|
||||
synchronized (this) {
|
||||
return top = new Claim(value, top);
|
||||
}
|
||||
}
|
||||
|
||||
public T getTopmost() {
|
||||
return content.isEmpty() ? null : content.get(content.size() - 1).value;
|
||||
Claim tp = top;
|
||||
return tp == null ? null : tp.value;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return content.isEmpty();
|
||||
return top == null;
|
||||
}
|
||||
|
||||
public class Claim {
|
||||
private Claim prev, next;
|
||||
private final T value;
|
||||
private final AtomicBoolean active = new AtomicBoolean(true);
|
||||
private boolean active = true;
|
||||
|
||||
private Claim(T value) {
|
||||
private Claim(T value, Claim prev) {
|
||||
// This is synchronized on the pool, so we can safely read top
|
||||
this.value = value;
|
||||
content.add(this);
|
||||
if (prev != null) {
|
||||
this.prev = prev;
|
||||
prev.next = this;
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (!active.getAndSet(false))
|
||||
throw new UnsupportedOperationException("Cannot release claim that is already released");
|
||||
content.remove(this);
|
||||
// By synchronizing from left to right, we ensure that only claims to our left can be blocked by this claim,
|
||||
// meaning that the rightmost claim can always be released, preventing deadlocks
|
||||
|
||||
// If there is no previous object, prev cannot change, so we can safely read it without synchronization
|
||||
// (which is effectively what is caused by synchronizing on this twice)
|
||||
synchronized (Objects.requireNonNullElse(prev, this)) {
|
||||
synchronized (this) {
|
||||
// If there is no next object, ClaimPool might add a new one, so we need to synchronize on it
|
||||
synchronized (Objects.requireNonNullElse(next, ClaimPool.this)) {
|
||||
if (!active) throw new UnsupportedOperationException("Cannot release claim that is already released");
|
||||
active = false;
|
||||
if (prev != null) prev.next = next;
|
||||
if (next != null) next.prev = prev;
|
||||
if (top == this) top = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("ClaimPool{");
|
||||
Claim current = top;
|
||||
while (current != null) {
|
||||
sb.append(current.value).append(" -> ");
|
||||
current = current.prev;
|
||||
}
|
||||
return sb.append('}').toString();
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package io.gitlab.jfronny.libjf.mainhttp.impl.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.chars.Char2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.bytes.Byte2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.bytes.Byte2ObjectMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -9,11 +9,11 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Trie<T> {
|
||||
public final Char2ObjectMap<Trie<T>> next;
|
||||
public final Byte2ObjectMap<Trie<T>> next;
|
||||
public T content;
|
||||
|
||||
public Trie() {
|
||||
this.next = new Char2ObjectArrayMap<>();
|
||||
this.next = new Byte2ObjectArrayMap<>();
|
||||
}
|
||||
|
||||
public void add(Map<String, T> next) {
|
||||
@ -22,7 +22,9 @@ public class Trie<T> {
|
||||
|
||||
public void add(String key, T value) {
|
||||
if (key.isEmpty()) this.content = value;
|
||||
else this.next.computeIfAbsent(key.charAt(0), k -> new Trie<>())
|
||||
char c = key.charAt(0);
|
||||
if (c > 255) throw new IllegalArgumentException("Invalid character: " + c);
|
||||
else this.next.computeIfAbsent((byte) c, k -> new Trie<>())
|
||||
.add(key.substring(1), value);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user