1 /*
2  * Copyright 2014 The Netty Project
3  *
4  * The Netty Project licenses this file to you under the Apache License,
5  * version 2.0 (the "License"); you may not use this file except in compliance
6  * with the License. You may obtain a copy of the License at:
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */

16 package io.netty.channel.unix;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelOutboundBuffer.MessageProcessor;
20 import io.netty.util.internal.PlatformDependent;
21
22 import java.nio.ByteBuffer;
23
24 import static io.netty.channel.unix.Limits.IOV_MAX;
25 import static io.netty.channel.unix.Limits.SSIZE_MAX;
26 import static io.netty.util.internal.ObjectUtil.checkPositive;
27 import static java.lang.Math.min;
28
29 /**
30  * Represent an array of struct array and so can be passed directly over via JNI without the need to do any more
31  * array copies.
32  *
33  * The buffers are written out directly into direct memory to match the struct iov. See also {@code man writev}.
34  *
35  * <pre>
36  * struct iovec {
37  *   void  *iov_base;
38  *   size_t iov_len;
39  * };
40  * </pre>
41  *
42  * See also
43  * <a href="http://rkennke.wordpress.com/2007/07/30/efficient-jni-programming-iv-wrapping-native-data-objects/"
44  * >Efficient JNI programming IV: Wrapping native data objects</a>.
45  */

46 public final class IovArray implements MessageProcessor {
47
48     /** The size of an address which should be 8 for 64 bits and 4 for 32 bits. */
49     private static final int ADDRESS_SIZE = Buffer.addressSize();
50
51     /**
52      * The size of an {@code iovec} struct in bytes. This is calculated as we have 2 entries each of the size of the
53      * address.
54      */

55     private static final int IOV_SIZE = 2 * ADDRESS_SIZE;
56
57     /**
58      * The needed memory to hold up to {@code IOV_MAX} iov entries, where {@code IOV_MAX} signified
59      * the maximum number of {@code iovec} structs that can be passed to {@code writev(...)}.
60      */

61     private static final int CAPACITY = IOV_MAX * IOV_SIZE;
62
63     private final ByteBuffer memory;
64     private final long memoryAddress;
65     private int count;
66     private long size;
67     private long maxBytes = SSIZE_MAX;
68
69     public IovArray() {
70         memory = Buffer.allocateDirectWithNativeOrder(CAPACITY);
71         memoryAddress = Buffer.memoryAddress(memory);
72     }
73
74     public void clear() {
75         count = 0;
76         size = 0;
77     }
78
79     /**
80      * @deprecated Use {@link #add(ByteBuf, intint)}
81      */

82     @Deprecated
83     public boolean add(ByteBuf buf) {
84         return add(buf, buf.readerIndex(), buf.readableBytes());
85     }
86
87     public boolean add(ByteBuf buf, int offset, int len) {
88         if (count == IOV_MAX) {
89             // No more room!
90             return false;
91         } else if (buf.nioBufferCount() == 1) {
92             if (len == 0) {
93                 return true;
94             }
95             if (buf.hasMemoryAddress()) {
96                 return add(buf.memoryAddress() + offset, len);
97             } else {
98                 ByteBuffer nioBuffer = buf.internalNioBuffer(offset, len);
99                 return add(Buffer.memoryAddress(nioBuffer) + nioBuffer.position(), len);
100             }
101         } else {
102             ByteBuffer[] buffers = buf.nioBuffers(offset, len);
103             for (ByteBuffer nioBuffer : buffers) {
104                 final int remaining = nioBuffer.remaining();
105                 if (remaining != 0 &&
106                         (!add(Buffer.memoryAddress(nioBuffer) + nioBuffer.position(), remaining) || count == IOV_MAX)) {
107                     return false;
108                 }
109             }
110             return true;
111         }
112     }
113
114     private boolean add(long addr, int len) {
115         assert addr != 0;
116
117         // If there is at least 1 entry then we enforce the maximum bytes. We want to accept at least one entry so we
118         // will attempt to write some data and make progress.
119         if (maxBytes - len < size && count > 0) {
120             // If the size + len will overflow SSIZE_MAX we stop populate the IovArray. This is done as linux
121             //  not allow to write more bytes then SSIZE_MAX with one writev(...) call and so will
122             // return 'EINVAL', which will raise an IOException.
123             //
124             // See also:
125             // - http://linux.die.net/man/2/writev
126             return false;
127         }
128         final int baseOffset = idx(count);
129         final int lengthOffset = baseOffset + ADDRESS_SIZE;
130
131         size += len;
132         ++count;
133
134         if (ADDRESS_SIZE == 8) {
135             // 64bit
136             if (PlatformDependent.hasUnsafe()) {
137                 PlatformDependent.putLong(baseOffset + memoryAddress, addr);
138                 PlatformDependent.putLong(lengthOffset + memoryAddress, len);
139             } else {
140                 memory.putLong(baseOffset, addr);
141                 memory.putLong(lengthOffset, len);
142             }
143         } else {
144             assert ADDRESS_SIZE == 4;
145             if (PlatformDependent.hasUnsafe()) {
146                 PlatformDependent.putInt(baseOffset + memoryAddress, (int) addr);
147                 PlatformDependent.putInt(lengthOffset + memoryAddress, len);
148             } else {
149                 memory.putInt(baseOffset, (int) addr);
150                 memory.putInt(lengthOffset, len);
151             }
152         }
153         return true;
154     }
155
156     /**
157      * Returns the number if iov entries.
158      */

159     public int count() {
160         return count;
161     }
162
163     /**
164      * Returns the size in bytes
165      */

166     public long size() {
167         return size;
168     }
169
170     /**
171      * Set the maximum amount of bytes that can be added to this {@link IovArray} via {@link #add(ByteBuf, intint)}
172      * <p>
173      * This will not impact the existing state of the {@link IovArray}, and only applies to subsequent calls to
174      * {@link #add(ByteBuf)}.
175      * <p>
176      * In order to ensure some progress is made at least one {@link ByteBuf} will be accepted even if it's size exceeds
177      * this value.
178      * @param maxBytes the maximum amount of bytes that can be added to this {@link IovArray}.
179      */

180     public void maxBytes(long maxBytes) {
181         this.maxBytes = min(SSIZE_MAX, checkPositive(maxBytes, "maxBytes"));
182     }
183
184     /**
185      * Get the maximum amount of bytes that can be added to this {@link IovArray}.
186      * @return the maximum amount of bytes that can be added to this {@link IovArray}.
187      */

188     public long maxBytes() {
189         return maxBytes;
190     }
191
192     /**
193      * Returns the {@code memoryAddress} for the given {@code offset}.
194      */

195     public long memoryAddress(int offset) {
196         return memoryAddress + idx(offset);
197     }
198
199     /**
200      * Release the {@link IovArray}. Once release further using of it may crash the JVM!
201      */

202     public void release() {
203         Buffer.free(memory);
204     }
205
206     @Override
207     public boolean processMessage(Object msg) throws Exception {
208         if (msg instanceof ByteBuf) {
209             ByteBuf buffer = (ByteBuf) msg;
210             return add(buffer, buffer.readerIndex(), buffer.readableBytes());
211         }
212         return false;
213     }
214
215     private static int idx(int index) {
216         return IOV_SIZE * index;
217     }
218 }
219