1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.uring;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelOutboundBuffer;
20 import io.netty.util.ReferenceCountUtil;
21 import io.netty.util.ReferenceCounted;
22 import io.netty.util.collection.LongObjectHashMap;
23
24 import java.util.Arrays;
25
26 import static io.netty.channel.uring.AbstractIoUringStreamChannel.IovBufferCollector.remainingIovs;
27
28
29
30
31
32
33
34
35
36
37
38
39
40 final class PendingZeroCopyWrites {
41 private static final int MAX_POOLED_ID = Short.MAX_VALUE;
42
43 private PendingWrite[] pooled;
44 private short[] freeIdStack;
45
46 private int freeStackTop;
47
48 private long nextOverflowId = MAX_POOLED_ID + 1L;
49 private LongObjectHashMap<PendingWrite> overflow;
50
51 PendingZeroCopyWrites() {
52 pooled = new PendingWrite[4];
53 freeIdStack = new short[pooled.length];
54
55 for (int id = pooled.length - 1; id > 0; id--) {
56 freeIdStack[freeStackTop++] = (short) id;
57 }
58 }
59
60 long nextUserData() {
61 if (freeStackTop > 0) {
62 return freeIdStack[--freeStackTop];
63 }
64 return nextUserDataSlow();
65 }
66
67 private long nextUserDataSlow() {
68 int oldCapacity = pooled.length;
69 if (oldCapacity == MAX_POOLED_ID + 1) {
70 return nextOverflowId++;
71 }
72 int newCapacity = oldCapacity << 1;
73 pooled = Arrays.copyOf(pooled, newCapacity);
74 freeIdStack = new short[newCapacity];
75 for (int id = newCapacity - 1; id >= oldCapacity; id--) {
76 freeIdStack[freeStackTop++] = (short) id;
77 }
78 return nextUserData();
79 }
80
81 PendingWrite register(long userData) {
82 if (userData > MAX_POOLED_ID) {
83 return registerOverflow(userData);
84 }
85 int id = (int) userData;
86 PendingWrite write = pooled[id];
87 if (write == null) {
88 pooled[id] = write = new PendingWrite();
89 }
90 return write;
91 }
92
93
94 private PendingWrite registerOverflow(long userData) {
95 if (overflow == null) {
96 overflow = new LongObjectHashMap<>(2);
97 }
98 PendingWrite write = new PendingWrite();
99 overflow.put(userData, write);
100 return write;
101 }
102
103 void release(long userData) {
104 PendingWrite write = userData <= MAX_POOLED_ID ?
105 pooled[(int) userData] : overflow.remove(userData);
106 write.release();
107 recycle(userData);
108 }
109
110
111 void recycle(long userData) {
112 if (userData <= MAX_POOLED_ID) {
113 freeIdStack[freeStackTop++] = (short) userData;
114 }
115 }
116
117 static final class PendingWrite implements ChannelOutboundBuffer.MessageProcessor {
118 private ReferenceCounted[] references = new ReferenceCounted[1];
119 private int count;
120 private int remaining;
121
122 void retain(ChannelOutboundBuffer buffer, int iovCount) {
123 if (iovCount == 0) {
124 return;
125 }
126 ByteBuf first = (ByteBuf) buffer.current();
127 if (buffer.size() == 1 || iovCount == 1 && first.isReadable()) {
128 add(first.retain());
129 return;
130 }
131 remaining = iovCount;
132 try {
133 buffer.forEachFlushedMessage(this);
134 } catch (Exception e) {
135 throw new IllegalStateException(e);
136 }
137 }
138
139 @Override
140 public boolean processMessage(Object msg) {
141 ByteBuf buf = (ByteBuf) msg;
142 if (buf.isReadable()) {
143 add(buf.retain());
144 remaining = remainingIovs(buf, remaining);
145 }
146 return remaining != 0;
147 }
148
149 void add(ReferenceCounted reference) {
150 if (count == references.length) {
151 references = Arrays.copyOf(references, count << 1);
152 }
153 references[count++] = reference;
154 }
155
156 private void release() {
157 for (int i = 0; i < count; i++) {
158 ReferenceCountUtil.safeRelease(references[i]);
159 references[i] = null;
160 }
161 count = 0;
162 }
163 }
164 }