View Javadoc
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 io.netty.util.internal.PlatformDependent.allocateMemory;
28  import static io.netty.util.internal.PlatformDependent.directBufferAddress;
29  import static io.netty.util.internal.PlatformDependent.freeMemory;
30  import static io.netty.util.internal.PlatformDependent.putInt;
31  import static io.netty.util.internal.PlatformDependent.putLong;
32  import static java.lang.Math.min;
33  
34  /**
35   * Represent an array of struct array and so can be passed directly over via JNI without the need to do any more
36   * array copies.
37   *
38   * The buffers are written out directly into direct memory to match the struct iov. See also {@code man writev}.
39   *
40   * <pre>
41   * struct iovec {
42   *   void  *iov_base;
43   *   size_t iov_len;
44   * };
45   * </pre>
46   *
47   * See also
48   * <a href="http://rkennke.wordpress.com/2007/07/30/efficient-jni-programming-iv-wrapping-native-data-objects/"
49   * >Efficient JNI programming IV: Wrapping native data objects</a>.
50   */
51  public final class IovArray implements MessageProcessor {
52  
53      /** The size of an address which should be 8 for 64 bits and 4 for 32 bits. */
54      private static final int ADDRESS_SIZE = PlatformDependent.addressSize();
55  
56      /**
57       * The size of an {@code iovec} struct in bytes. This is calculated as we have 2 entries each of the size of the
58       * address.
59       */
60      private static final int IOV_SIZE = 2 * ADDRESS_SIZE;
61  
62      /**
63       * The needed memory to hold up to {@code IOV_MAX} iov entries, where {@code IOV_MAX} signified
64       * the maximum number of {@code iovec} structs that can be passed to {@code writev(...)}.
65       */
66      private static final int CAPACITY = IOV_MAX * IOV_SIZE;
67  
68      private final long memoryAddress;
69      private int count;
70      private long size;
71      private long maxBytes = SSIZE_MAX;
72  
73      public IovArray() {
74          memoryAddress = allocateMemory(CAPACITY);
75      }
76  
77      public void clear() {
78          count = 0;
79          size = 0;
80      }
81  
82      /**
83       * Try to add the given {@link ByteBuf}. Returns {@code true} on success,
84       * {@code false} otherwise.
85       */
86      public boolean add(ByteBuf buf) {
87          if (count == IOV_MAX) {
88              // No more room!
89              return false;
90          } else if (buf.nioBufferCount() == 1) {
91              final int len = buf.readableBytes();
92              return len == 0 || add(buf.memoryAddress(), buf.readerIndex(), len);
93          } else {
94              ByteBuffer[] buffers = buf.nioBuffers();
95              for (ByteBuffer nioBuffer : buffers) {
96                  final int len = nioBuffer.remaining();
97                  if (len != 0 && (!add(directBufferAddress(nioBuffer), nioBuffer.position(), len) || count == IOV_MAX)) {
98                      break;
99                  }
100             }
101             return true;
102         }
103     }
104 
105     private boolean add(long addr, int offset, int len) {
106         if (len == 0) {
107             // No need to add an empty buffer.
108             return true;
109         }
110 
111         final long baseOffset = memoryAddress(count);
112         final long lengthOffset = baseOffset + ADDRESS_SIZE;
113 
114         // If there is at least 1 entry then we enforce the maximum bytes. We want to accept at least one entry so we
115         // will attempt to write some data and make progress.
116         if (maxBytes - len < size && count > 0) {
117             // If the size + len will overflow SSIZE_MAX we stop populate the IovArray. This is done as linux
118             //  not allow to write more bytes then SSIZE_MAX with one writev(...) call and so will
119             // return 'EINVAL', which will raise an IOException.
120             //
121             // See also:
122             // - http://linux.die.net/man/2/writev
123             return false;
124         }
125         size += len;
126         ++count;
127 
128         if (ADDRESS_SIZE == 8) {
129             // 64bit
130             putLong(baseOffset, addr + offset);
131             putLong(lengthOffset, len);
132         } else {
133             assert ADDRESS_SIZE == 4;
134             putInt(baseOffset, (int) addr + offset);
135             putInt(lengthOffset, len);
136         }
137         return true;
138     }
139 
140     /**
141      * Returns the number if iov entries.
142      */
143     public int count() {
144         return count;
145     }
146 
147     /**
148      * Returns the size in bytes
149      */
150     public long size() {
151         return size;
152     }
153 
154     /**
155      * Set the maximum amount of bytes that can be added to this {@link IovArray} via {@link #add(ByteBuf)}.
156      * <p>
157      * This will not impact the existing state of the {@link IovArray}, and only applies to subsequent calls to
158      * {@link #add(ByteBuf)}.
159      * <p>
160      * In order to ensure some progress is made at least one {@link ByteBuf} will be accepted even if it's size exceeds
161      * this value.
162      * @param maxBytes the maximum amount of bytes that can be added to this {@link IovArray} via {@link #add(ByteBuf)}.
163      */
164     public void maxBytes(long maxBytes) {
165         this.maxBytes = min(SSIZE_MAX, checkPositive(maxBytes, "maxBytes"));
166     }
167 
168     /**
169      * Get the maximum amount of bytes that can be added to this {@link IovArray} via {@link #add(ByteBuf)}.
170      * @return the maximum amount of bytes that can be added to this {@link IovArray} via {@link #add(ByteBuf)}.
171      */
172     public long maxBytes() {
173         return maxBytes;
174     }
175 
176     /**
177      * Returns the {@code memoryAddress} for the given {@code offset}.
178      */
179     public long memoryAddress(int offset) {
180         return memoryAddress + IOV_SIZE * offset;
181     }
182 
183     /**
184      * Release the {@link IovArray}. Once release further using of it may crash the JVM!
185      */
186     public void release() {
187         freeMemory(memoryAddress);
188     }
189 
190     @Override
191     public boolean processMessage(Object msg) throws Exception {
192         return (msg instanceof ByteBuf) && add((ByteBuf) msg);
193     }
194 }