001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.io.buffer;
018
019import java.io.InputStream;
020import java.io.OutputStream;
021import java.nio.ByteBuffer;
022import java.nio.charset.Charset;
023import java.nio.charset.StandardCharsets;
024import java.util.function.IntPredicate;
025
026import io.netty.buffer.ByteBuf;
027import io.netty.buffer.ByteBufInputStream;
028import io.netty.buffer.ByteBufOutputStream;
029import io.netty.buffer.ByteBufUtil;
030
031import org.springframework.lang.Nullable;
032import org.springframework.util.Assert;
033import org.springframework.util.ObjectUtils;
034
035/**
036 * Implementation of the {@code DataBuffer} interface that wraps a Netty
037 * {@link ByteBuf}. Typically constructed with {@link NettyDataBufferFactory}.
038 *
039 * @author Arjen Poutsma
040 * @author Brian Clozel
041 * @since 5.0
042 */
043public class NettyDataBuffer implements PooledDataBuffer {
044
045        private final ByteBuf byteBuf;
046
047        private final NettyDataBufferFactory dataBufferFactory;
048
049
050        /**
051         * Create a new {@code NettyDataBuffer} based on the given {@code ByteBuff}.
052         * @param byteBuf the buffer to base this buffer on
053         */
054        NettyDataBuffer(ByteBuf byteBuf, NettyDataBufferFactory dataBufferFactory) {
055                Assert.notNull(byteBuf, "ByteBuf must not be null");
056                Assert.notNull(dataBufferFactory, "NettyDataBufferFactory must not be null");
057                this.byteBuf = byteBuf;
058                this.dataBufferFactory = dataBufferFactory;
059        }
060
061
062        /**
063         * Directly exposes the native {@code ByteBuf} that this buffer is based on.
064         * @return the wrapped byte buffer
065         */
066        public ByteBuf getNativeBuffer() {
067                return this.byteBuf;
068        }
069
070        @Override
071        public NettyDataBufferFactory factory() {
072                return this.dataBufferFactory;
073        }
074
075        @Override
076        public int indexOf(IntPredicate predicate, int fromIndex) {
077                Assert.notNull(predicate, "IntPredicate must not be null");
078                if (fromIndex < 0) {
079                        fromIndex = 0;
080                }
081                else if (fromIndex >= this.byteBuf.writerIndex()) {
082                        return -1;
083                }
084                int length = this.byteBuf.writerIndex() - fromIndex;
085                return this.byteBuf.forEachByte(fromIndex, length, predicate.negate()::test);
086        }
087
088        @Override
089        public int lastIndexOf(IntPredicate predicate, int fromIndex) {
090                Assert.notNull(predicate, "IntPredicate must not be null");
091                if (fromIndex < 0) {
092                        return -1;
093                }
094                fromIndex = Math.min(fromIndex, this.byteBuf.writerIndex() - 1);
095                return this.byteBuf.forEachByteDesc(0, fromIndex + 1, predicate.negate()::test);
096        }
097
098        @Override
099        public int readableByteCount() {
100                return this.byteBuf.readableBytes();
101        }
102
103        @Override
104        public int writableByteCount() {
105                return this.byteBuf.writableBytes();
106        }
107
108        @Override
109        public int readPosition() {
110                return this.byteBuf.readerIndex();
111        }
112
113        @Override
114        public NettyDataBuffer readPosition(int readPosition) {
115                this.byteBuf.readerIndex(readPosition);
116                return this;
117        }
118
119        @Override
120        public int writePosition() {
121                return this.byteBuf.writerIndex();
122        }
123
124        @Override
125        public NettyDataBuffer writePosition(int writePosition) {
126                this.byteBuf.writerIndex(writePosition);
127                return this;
128        }
129
130        @Override
131        public byte getByte(int index) {
132                return this.byteBuf.getByte(index);
133        }
134
135        @Override
136        public int capacity() {
137                return this.byteBuf.capacity();
138        }
139
140        @Override
141        public NettyDataBuffer capacity(int capacity) {
142                this.byteBuf.capacity(capacity);
143                return this;
144        }
145
146        @Override
147        public DataBuffer ensureCapacity(int capacity) {
148                this.byteBuf.ensureWritable(capacity);
149                return this;
150        }
151
152        @Override
153        public byte read() {
154                return this.byteBuf.readByte();
155        }
156
157        @Override
158        public NettyDataBuffer read(byte[] destination) {
159                this.byteBuf.readBytes(destination);
160                return this;
161        }
162
163        @Override
164        public NettyDataBuffer read(byte[] destination, int offset, int length) {
165                this.byteBuf.readBytes(destination, offset, length);
166                return this;
167        }
168
169        @Override
170        public NettyDataBuffer write(byte b) {
171                this.byteBuf.writeByte(b);
172                return this;
173        }
174
175        @Override
176        public NettyDataBuffer write(byte[] source) {
177                this.byteBuf.writeBytes(source);
178                return this;
179        }
180
181        @Override
182        public NettyDataBuffer write(byte[] source, int offset, int length) {
183                this.byteBuf.writeBytes(source, offset, length);
184                return this;
185        }
186
187        @Override
188        public NettyDataBuffer write(DataBuffer... buffers) {
189                if (!ObjectUtils.isEmpty(buffers)) {
190                        if (hasNettyDataBuffers(buffers)) {
191                                ByteBuf[] nativeBuffers = new ByteBuf[buffers.length];
192                                for (int i = 0; i < buffers.length; i++) {
193                                        nativeBuffers[i] = ((NettyDataBuffer) buffers[i]).getNativeBuffer();
194                                }
195                                write(nativeBuffers);
196                        }
197                        else {
198                                ByteBuffer[] byteBuffers = new ByteBuffer[buffers.length];
199                                for (int i = 0; i < buffers.length; i++) {
200                                        byteBuffers[i] = buffers[i].asByteBuffer();
201
202                                }
203                                write(byteBuffers);
204                        }
205                }
206                return this;
207        }
208
209        private static boolean hasNettyDataBuffers(DataBuffer[] buffers) {
210                for (DataBuffer buffer : buffers) {
211                        if (!(buffer instanceof NettyDataBuffer)) {
212                                return false;
213                        }
214                }
215                return true;
216        }
217
218        @Override
219        public NettyDataBuffer write(ByteBuffer... buffers) {
220                if (!ObjectUtils.isEmpty(buffers)) {
221                        for (ByteBuffer buffer : buffers) {
222                                this.byteBuf.writeBytes(buffer);
223                        }
224                }
225                return this;
226        }
227
228        /**
229         * Writes one or more Netty {@link ByteBuf ByteBufs} to this buffer,
230         * starting at the current writing position.
231         * @param byteBufs the buffers to write into this buffer
232         * @return this buffer
233         */
234        public NettyDataBuffer write(ByteBuf... byteBufs) {
235                if (!ObjectUtils.isEmpty(byteBufs)) {
236                        for (ByteBuf byteBuf : byteBufs) {
237                                this.byteBuf.writeBytes(byteBuf);
238                        }
239                }
240                return this;
241        }
242
243        @Override
244        public DataBuffer write(CharSequence charSequence, Charset charset) {
245                Assert.notNull(charSequence, "CharSequence must not be null");
246                Assert.notNull(charset, "Charset must not be null");
247                if (StandardCharsets.UTF_8.equals(charset)) {
248                        ByteBufUtil.writeUtf8(this.byteBuf, charSequence);
249                }
250                else if (StandardCharsets.US_ASCII.equals(charset)) {
251                        ByteBufUtil.writeAscii(this.byteBuf, charSequence);
252                }
253                else {
254                        return PooledDataBuffer.super.write(charSequence, charset);
255                }
256                return this;
257        }
258
259        @Override
260        public NettyDataBuffer slice(int index, int length) {
261                ByteBuf slice = this.byteBuf.slice(index, length);
262                return new NettyDataBuffer(slice, this.dataBufferFactory);
263        }
264
265        @Override
266        public NettyDataBuffer retainedSlice(int index, int length) {
267                ByteBuf slice = this.byteBuf.retainedSlice(index, length);
268                return new NettyDataBuffer(slice, this.dataBufferFactory);
269        }
270
271        @Override
272        public ByteBuffer asByteBuffer() {
273                return this.byteBuf.nioBuffer();
274        }
275
276        @Override
277        public ByteBuffer asByteBuffer(int index, int length) {
278                return this.byteBuf.nioBuffer(index, length);
279        }
280
281        @Override
282        public InputStream asInputStream() {
283                return new ByteBufInputStream(this.byteBuf);
284        }
285
286        @Override
287        public InputStream asInputStream(boolean releaseOnClose) {
288                return new ByteBufInputStream(this.byteBuf, releaseOnClose);
289        }
290
291        @Override
292        public OutputStream asOutputStream() {
293                return new ByteBufOutputStream(this.byteBuf);
294        }
295
296        @Override
297        public String toString(Charset charset) {
298                Assert.notNull(charset, "Charset must not be null");
299                return this.byteBuf.toString(charset);
300        }
301
302        @Override
303        public String toString(int index, int length, Charset charset) {
304                Assert.notNull(charset, "Charset must not be null");
305                return this.byteBuf.toString(index, length, charset);
306        }
307
308        @Override
309        public boolean isAllocated() {
310                return this.byteBuf.refCnt() > 0;
311        }
312
313        @Override
314        public PooledDataBuffer retain() {
315                return new NettyDataBuffer(this.byteBuf.retain(), this.dataBufferFactory);
316        }
317
318        @Override
319        public boolean release() {
320                return this.byteBuf.release();
321        }
322
323
324        @Override
325        public boolean equals(@Nullable Object other) {
326                return (this == other || (other instanceof NettyDataBuffer &&
327                                this.byteBuf.equals(((NettyDataBuffer) other).byteBuf)));
328        }
329
330        @Override
331        public int hashCode() {
332                return this.byteBuf.hashCode();
333        }
334
335        @Override
336        public String toString() {
337                return this.byteBuf.toString();
338        }
339
340}