• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import static com.google.protobuf.Internal.checkNotNull;
34 
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.InvalidObjectException;
38 import java.io.ObjectInputStream;
39 import java.io.OutputStream;
40 import java.nio.ByteBuffer;
41 import java.nio.ByteOrder;
42 import java.nio.InvalidMarkException;
43 import java.nio.charset.Charset;
44 import java.util.Collections;
45 import java.util.List;
46 
47 /** A {@link ByteString} that wraps around a {@link ByteBuffer}. */
48 final class NioByteString extends ByteString.LeafByteString {
49   private final ByteBuffer buffer;
50 
NioByteString(ByteBuffer buffer)51   NioByteString(ByteBuffer buffer) {
52     checkNotNull(buffer, "buffer");
53 
54     // Use native byte order for fast fixed32/64 operations.
55     this.buffer = buffer.slice().order(ByteOrder.nativeOrder());
56   }
57 
58   // =================================================================
59   // Serializable
60 
61   /** Magic method that lets us override serialization behavior. */
writeReplace()62   private Object writeReplace() {
63     return ByteString.copyFrom(buffer.slice());
64   }
65 
66   /** Magic method that lets us override deserialization behavior. */
readObject(@uppressWarnings"unused") ObjectInputStream in)67   private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException {
68     throw new InvalidObjectException("NioByteString instances are not to be serialized directly");
69   }
70 
71   // =================================================================
72 
73   @Override
byteAt(int index)74   public byte byteAt(int index) {
75     try {
76       return buffer.get(index);
77     } catch (ArrayIndexOutOfBoundsException e) {
78       throw e;
79     } catch (IndexOutOfBoundsException e) {
80       throw new ArrayIndexOutOfBoundsException(e.getMessage());
81     }
82   }
83 
84   @Override
internalByteAt(int index)85   public byte internalByteAt(int index) {
86     // it isn't possible to avoid the bounds checking inside of ByteBuffer, so just use the default
87     // implementation.
88     return byteAt(index);
89   }
90 
91   @Override
size()92   public int size() {
93     return buffer.remaining();
94   }
95 
96   @Override
substring(int beginIndex, int endIndex)97   public ByteString substring(int beginIndex, int endIndex) {
98     try {
99       ByteBuffer slice = slice(beginIndex, endIndex);
100       return new NioByteString(slice);
101     } catch (ArrayIndexOutOfBoundsException e) {
102       throw e;
103     } catch (IndexOutOfBoundsException e) {
104       throw new ArrayIndexOutOfBoundsException(e.getMessage());
105     }
106   }
107 
108   @Override
copyToInternal( byte[] target, int sourceOffset, int targetOffset, int numberToCopy)109   protected void copyToInternal(
110       byte[] target, int sourceOffset, int targetOffset, int numberToCopy) {
111     ByteBuffer slice = buffer.slice();
112     slice.position(sourceOffset);
113     slice.get(target, targetOffset, numberToCopy);
114   }
115 
116   @Override
copyTo(ByteBuffer target)117   public void copyTo(ByteBuffer target) {
118     target.put(buffer.slice());
119   }
120 
121   @Override
writeTo(OutputStream out)122   public void writeTo(OutputStream out) throws IOException {
123     out.write(toByteArray());
124   }
125 
126   @Override
equalsRange(ByteString other, int offset, int length)127   boolean equalsRange(ByteString other, int offset, int length) {
128     return substring(0, length).equals(other.substring(offset, offset + length));
129   }
130 
131   @Override
writeToInternal(OutputStream out, int sourceOffset, int numberToWrite)132   void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException {
133     if (buffer.hasArray()) {
134       // Optimized write for array-backed buffers.
135       // Note that we're taking the risk that a malicious OutputStream could modify the array.
136       int bufferOffset = buffer.arrayOffset() + buffer.position() + sourceOffset;
137       out.write(buffer.array(), bufferOffset, numberToWrite);
138       return;
139     }
140 
141     ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out);
142   }
143 
144   @Override
writeTo(ByteOutput output)145   void writeTo(ByteOutput output) throws IOException {
146     output.writeLazy(buffer.slice());
147   }
148 
149   @Override
asReadOnlyByteBuffer()150   public ByteBuffer asReadOnlyByteBuffer() {
151     return buffer.asReadOnlyBuffer();
152   }
153 
154   @Override
asReadOnlyByteBufferList()155   public List<ByteBuffer> asReadOnlyByteBufferList() {
156     return Collections.singletonList(asReadOnlyByteBuffer());
157   }
158 
159   @Override
toStringInternal(Charset charset)160   protected String toStringInternal(Charset charset) {
161     final byte[] bytes;
162     final int offset;
163     final int length;
164     if (buffer.hasArray()) {
165       bytes = buffer.array();
166       offset = buffer.arrayOffset() + buffer.position();
167       length = buffer.remaining();
168     } else {
169       // TODO(nathanmittler): Can we optimize this?
170       bytes = toByteArray();
171       offset = 0;
172       length = bytes.length;
173     }
174     return new String(bytes, offset, length, charset);
175   }
176 
177   @Override
isValidUtf8()178   public boolean isValidUtf8() {
179     return Utf8.isValidUtf8(buffer);
180   }
181 
182   @Override
partialIsValidUtf8(int state, int offset, int length)183   protected int partialIsValidUtf8(int state, int offset, int length) {
184     return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length);
185   }
186 
187   @Override
equals(Object other)188   public boolean equals(Object other) {
189     if (other == this) {
190       return true;
191     }
192     if (!(other instanceof ByteString)) {
193       return false;
194     }
195     ByteString otherString = ((ByteString) other);
196     if (size() != otherString.size()) {
197       return false;
198     }
199     if (size() == 0) {
200       return true;
201     }
202     if (other instanceof NioByteString) {
203       return buffer.equals(((NioByteString) other).buffer);
204     }
205     if (other instanceof RopeByteString) {
206       return other.equals(this);
207     }
208     return buffer.equals(otherString.asReadOnlyByteBuffer());
209   }
210 
211   @Override
partialHash(int h, int offset, int length)212   protected int partialHash(int h, int offset, int length) {
213     for (int i = offset; i < offset + length; i++) {
214       h = h * 31 + buffer.get(i);
215     }
216     return h;
217   }
218 
219   @Override
newInput()220   public InputStream newInput() {
221     return new InputStream() {
222       private final ByteBuffer buf = buffer.slice();
223 
224       @Override
225       public void mark(int readlimit) {
226         buf.mark();
227       }
228 
229       @Override
230       public boolean markSupported() {
231         return true;
232       }
233 
234       @Override
235       public void reset() throws IOException {
236         try {
237           buf.reset();
238         } catch (InvalidMarkException e) {
239           throw new IOException(e);
240         }
241       }
242 
243       @Override
244       public int available() throws IOException {
245         return buf.remaining();
246       }
247 
248       @Override
249       public int read() throws IOException {
250         if (!buf.hasRemaining()) {
251           return -1;
252         }
253         return buf.get() & 0xFF;
254       }
255 
256       @Override
257       public int read(byte[] bytes, int off, int len) throws IOException {
258         if (!buf.hasRemaining()) {
259           return -1;
260         }
261 
262         len = Math.min(len, buf.remaining());
263         buf.get(bytes, off, len);
264         return len;
265       }
266     };
267   }
268 
269   @Override
newCodedInput()270   public CodedInputStream newCodedInput() {
271     return CodedInputStream.newInstance(buffer, true);
272   }
273 
274   /**
275    * Creates a slice of a range of this buffer.
276    *
277    * @param beginIndex the beginning index of the slice (inclusive).
278    * @param endIndex the end index of the slice (exclusive).
279    * @return the requested slice.
280    */
slice(int beginIndex, int endIndex)281   private ByteBuffer slice(int beginIndex, int endIndex) {
282     if (beginIndex < buffer.position() || endIndex > buffer.limit() || beginIndex > endIndex) {
283       throw new IllegalArgumentException(
284           String.format("Invalid indices [%d, %d]", beginIndex, endIndex));
285     }
286 
287     ByteBuffer slice = buffer.slice();
288     slice.position(beginIndex - buffer.position());
289     slice.limit(endIndex - buffer.position());
290     return slice;
291   }
292 }
293