View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.util;
29  
30  import java.io.ByteArrayInputStream;
31  import java.io.ByteArrayOutputStream;
32  import java.io.ObjectInputStream;
33  import java.io.ObjectOutputStream;
34  import java.nio.ByteBuffer;
35  import java.nio.charset.StandardCharsets;
36  
37  import org.junit.Assert;
38  import org.junit.Test;
39  
40  /**
41   * Unit tests for {@link ByteArrayBuffer}.
42   *
43   */
44  public class TestByteArrayBuffer {
45  
46      @Test
47      public void testConstructor() throws Exception {
48          final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
49          Assert.assertEquals(16, buffer.capacity());
50          Assert.assertEquals(0, buffer.length());
51          Assert.assertNotNull(buffer.array());
52          Assert.assertEquals(16, buffer.array().length);
53          try {
54              new ByteArrayBuffer(-1);
55              Assert.fail("IllegalArgumentException should have been thrown");
56          } catch (final IllegalArgumentException ex) {
57              // expected
58          }
59      }
60  
61      @Test
62      public void testSimpleAppend() throws Exception {
63          final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
64          Assert.assertEquals(16, buffer.capacity());
65          Assert.assertEquals(0, buffer.length());
66          final byte[] b1 = buffer.toByteArray();
67          Assert.assertNotNull(b1);
68          Assert.assertEquals(0, b1.length);
69          Assert.assertTrue(buffer.isEmpty());
70          Assert.assertFalse(buffer.isFull());
71  
72          final byte[] tmp = new byte[] { 1, 2, 3, 4};
73          buffer.append(tmp, 0, tmp.length);
74          Assert.assertEquals(16, buffer.capacity());
75          Assert.assertEquals(4, buffer.length());
76          Assert.assertFalse(buffer.isEmpty());
77          Assert.assertFalse(buffer.isFull());
78  
79          final byte[] b2 = buffer.toByteArray();
80          Assert.assertNotNull(b2);
81          Assert.assertEquals(4, b2.length);
82          for (int i = 0; i < tmp.length; i++) {
83              Assert.assertEquals(tmp[i], b2[i]);
84              Assert.assertEquals(tmp[i], buffer.byteAt(i));
85          }
86          buffer.clear();
87          Assert.assertEquals(16, buffer.capacity());
88          Assert.assertEquals(0, buffer.length());
89          Assert.assertTrue(buffer.isEmpty());
90          Assert.assertFalse(buffer.isFull());
91      }
92  
93      @Test
94      public void testExpandAppend() throws Exception {
95          final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
96          Assert.assertEquals(4, buffer.capacity());
97  
98          final byte[] tmp = new byte[] { 1, 2, 3, 4};
99          buffer.append(tmp, 0, 2);
100         buffer.append(tmp, 0, 4);
101         buffer.append(tmp, 0, 0);
102 
103         Assert.assertEquals(8, buffer.capacity());
104         Assert.assertEquals(6, buffer.length());
105 
106         buffer.append(tmp, 0, 4);
107 
108         Assert.assertEquals(16, buffer.capacity());
109         Assert.assertEquals(10, buffer.length());
110     }
111 
112     @Test
113     public void testAppendHeapByteBuffer() {
114         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
115         Assert.assertEquals(4, buffer.capacity());
116 
117         final ByteBuffer tmp = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6});
118         buffer.append(tmp);
119 
120         Assert.assertFalse("The input buffer should be drained", tmp.hasRemaining());
121         Assert.assertEquals(8, buffer.capacity());
122         Assert.assertEquals(6, buffer.length());
123 
124         tmp.clear();
125         buffer.append(tmp);
126 
127         Assert.assertFalse("The input buffer should be drained", tmp.hasRemaining());
128         Assert.assertEquals(16, buffer.capacity());
129         Assert.assertEquals(12, buffer.length());
130         Assert.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray());
131     }
132 
133     @Test
134     public void testAppendHeapByteBufferWithOffset() {
135         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
136         Assert.assertEquals(4, buffer.capacity());
137 
138         final ByteBuffer tmp = ByteBuffer.wrap(new byte[] { 7, 7, 1, 2, 3, 4, 5, 6, 7, 7}, 2, 6).slice();
139         Assert.assertTrue("Validate this is testing a buffer with an array offset", tmp.arrayOffset() > 0);
140 
141         buffer.append(tmp);
142 
143         Assert.assertFalse("The input buffer should be drained", tmp.hasRemaining());
144         Assert.assertEquals(8, buffer.capacity());
145         Assert.assertEquals(6, buffer.length());
146 
147         tmp.clear();
148         Assert.assertEquals(6, tmp.remaining());
149         buffer.append(tmp);
150 
151         Assert.assertFalse("The input buffer should be drained", tmp.hasRemaining());
152         Assert.assertEquals(16, buffer.capacity());
153         Assert.assertEquals(12, buffer.length());
154         Assert.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray());
155     }
156 
157     @Test
158     public void testAppendDirectByteBuffer() {
159         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
160         Assert.assertEquals(4, buffer.capacity());
161 
162         final ByteBuffer tmp = ByteBuffer.allocateDirect(6);
163         tmp.put(new byte[] { 1, 2, 3, 4, 5, 6}).flip();
164         buffer.append(tmp);
165 
166         Assert.assertFalse("The input buffer should be drained", tmp.hasRemaining());
167         Assert.assertEquals(8, buffer.capacity());
168         Assert.assertEquals(6, buffer.length());
169 
170         tmp.clear();
171         buffer.append(tmp);
172 
173         Assert.assertFalse("The input buffer should be drained", tmp.hasRemaining());
174         Assert.assertEquals(16, buffer.capacity());
175         Assert.assertEquals(12, buffer.length());
176         Assert.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray());
177     }
178 
179     @Test
180     public void testInvalidAppend() throws Exception {
181         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
182         buffer.append((byte[])null, 0, 0);
183 
184         final byte[] tmp = new byte[] { 1, 2, 3, 4};
185         try {
186             buffer.append(tmp, -1, 0);
187             Assert.fail("IndexOutOfBoundsException should have been thrown");
188         } catch (final IndexOutOfBoundsException ex) {
189             // expected
190         }
191         try {
192             buffer.append(tmp, 0, -1);
193             Assert.fail("IndexOutOfBoundsException should have been thrown");
194         } catch (final IndexOutOfBoundsException ex) {
195             // expected
196         }
197         try {
198             buffer.append(tmp, 0, 8);
199             Assert.fail("IndexOutOfBoundsException should have been thrown");
200         } catch (final IndexOutOfBoundsException ex) {
201             // expected
202         }
203         try {
204             buffer.append(tmp, 10, Integer.MAX_VALUE);
205             Assert.fail("IndexOutOfBoundsException should have been thrown");
206         } catch (final IndexOutOfBoundsException ex) {
207             // expected
208         }
209         try {
210             buffer.append(tmp, 2, 4);
211             Assert.fail("IndexOutOfBoundsException should have been thrown");
212         } catch (final IndexOutOfBoundsException ex) {
213             // expected
214         }
215     }
216 
217     @Test
218     public void testAppendOneByte() throws Exception {
219         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
220         Assert.assertEquals(4, buffer.capacity());
221 
222         final byte[] tmp = new byte[] { 1, 127, -1, -128, 1, -2};
223         for (final byte element : tmp) {
224             buffer.append(element);
225         }
226         Assert.assertEquals(8, buffer.capacity());
227         Assert.assertEquals(6, buffer.length());
228 
229         for (int i = 0; i < tmp.length; i++) {
230             Assert.assertEquals(tmp[i], buffer.byteAt(i));
231         }
232     }
233 
234     @Test
235     public void testSetLength() throws Exception {
236         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
237         buffer.setLength(2);
238         Assert.assertEquals(2, buffer.length());
239     }
240 
241     @Test
242     public void testSetInvalidLength() throws Exception {
243         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
244         try {
245             buffer.setLength(-2);
246             Assert.fail("IndexOutOfBoundsException should have been thrown");
247         } catch (final IndexOutOfBoundsException ex) {
248             // expected
249         }
250         try {
251             buffer.setLength(200);
252             Assert.fail("IndexOutOfBoundsException should have been thrown");
253         } catch (final IndexOutOfBoundsException ex) {
254             // expected
255         }
256     }
257 
258     @Test
259     public void testEnsureCapacity() throws Exception {
260         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
261         buffer.ensureCapacity(2);
262         Assert.assertEquals(4, buffer.capacity());
263         buffer.ensureCapacity(8);
264         Assert.assertEquals(8, buffer.capacity());
265     }
266 
267     @Test
268     public void testIndexOf() throws Exception {
269         final byte COLON = (byte) ':';
270         final byte COMMA = (byte) ',';
271         final byte[] bytes = "name1: value1; name2: value2".getBytes(StandardCharsets.US_ASCII);
272         final int index1 = 5;
273         final int index2 = 20;
274 
275         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
276         buffer.append(bytes, 0, bytes.length);
277 
278         Assert.assertEquals(index1, buffer.indexOf(COLON));
279         Assert.assertEquals(-1, buffer.indexOf(COMMA));
280         Assert.assertEquals(index1, buffer.indexOf(COLON, -1, 11));
281         Assert.assertEquals(index1, buffer.indexOf(COLON, 0, 1000));
282         Assert.assertEquals(-1, buffer.indexOf(COLON, 2, 1));
283         Assert.assertEquals(index2, buffer.indexOf(COLON, index1 + 1, buffer.length()));
284     }
285 
286     @Test
287     public void testAppendCharArrayAsAscii() throws Exception {
288         final String s1 = "stuff";
289         final String s2 = " and more stuff";
290         final char[] b1 = s1.toCharArray();
291         final char[] b2 = s2.toCharArray();
292 
293         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
294         buffer.append(b1, 0, b1.length);
295         buffer.append(b2, 0, b2.length);
296 
297         Assert.assertEquals(s1 + s2, new String(buffer.toByteArray(), StandardCharsets.US_ASCII));
298     }
299 
300     @Test
301     public void testAppendNullCharArray() throws Exception {
302         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
303         buffer.append((char[])null, 0, 0);
304         Assert.assertEquals(0, buffer.length());
305     }
306 
307     @Test
308     public void testAppendEmptyCharArray() throws Exception {
309         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
310         buffer.append(new char[] {}, 0, 0);
311         Assert.assertEquals(0, buffer.length());
312     }
313 
314     @Test
315     public void testAppendNullCharArrayBuffer() throws Exception {
316         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
317         buffer.append((CharArrayBuffer)null, 0, 0);
318         Assert.assertEquals(0, buffer.length());
319     }
320 
321     @Test
322     public void testAppendNullByteBuffer() throws Exception {
323         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
324         final ByteBuffer nullBuffer = null;
325         buffer.append(nullBuffer);
326         Assert.assertEquals(0, buffer.length());
327     }
328 
329     @Test
330     public void testInvalidAppendCharArrayAsAscii() throws Exception {
331         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
332         buffer.append((char[])null, 0, 0);
333 
334         final char[] tmp = new char[] { '1', '2', '3', '4'};
335         try {
336             buffer.append(tmp, -1, 0);
337             Assert.fail("IndexOutOfBoundsException should have been thrown");
338         } catch (final IndexOutOfBoundsException ex) {
339             // expected
340         }
341         try {
342             buffer.append(tmp, 0, -1);
343             Assert.fail("IndexOutOfBoundsException should have been thrown");
344         } catch (final IndexOutOfBoundsException ex) {
345             // expected
346         }
347         try {
348             buffer.append(tmp, 0, 8);
349             Assert.fail("IndexOutOfBoundsException should have been thrown");
350         } catch (final IndexOutOfBoundsException ex) {
351             // expected
352         }
353         try {
354             buffer.append(tmp, 10, Integer.MAX_VALUE);
355             Assert.fail("IndexOutOfBoundsException should have been thrown");
356         } catch (final IndexOutOfBoundsException ex) {
357             // expected
358         }
359         try {
360             buffer.append(tmp, 2, 4);
361             Assert.fail("IndexOutOfBoundsException should have been thrown");
362         } catch (final IndexOutOfBoundsException ex) {
363             // expected
364         }
365     }
366 
367     @Test
368     public void testSerialization() throws Exception {
369         final ByteArrayBuffer orig = new ByteArrayBuffer(32);
370         orig.append(1);
371         orig.append(2);
372         orig.append(3);
373         final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream();
374         try (final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer)) {
375             outStream.writeObject(orig);
376         }
377         final byte[] raw = outbuffer.toByteArray();
378         final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw);
379         final ObjectInputStream inStream = new ObjectInputStream(inBuffer);
380         final ByteArrayBuffer clone = (ByteArrayBuffer) inStream.readObject();
381         Assert.assertEquals(orig.capacity(), clone.capacity());
382         Assert.assertEquals(orig.length(), clone.length());
383         final byte[] data = clone.toByteArray();
384         Assert.assertNotNull(data);
385         Assert.assertEquals(3, data.length);
386         Assert.assertEquals(1, data[0]);
387         Assert.assertEquals(2, data[1]);
388         Assert.assertEquals(3, data[2]);
389     }
390 
391     @Test
392     public void testControlCharFiltering() throws Exception {
393         final char[] chars = new char[256];
394         for (char i = 0; i < 256; i++) {
395             chars[i] = i;
396         }
397 
398         final byte[] bytes = asByteArray(chars);
399 
400         Assert.assertEquals(
401             "?????????\t??????????????????????"
402                 + " !\"#$%&'()*+,-./0123456789:;<=>?"
403                 + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
404                 + "`abcdefghijklmnopqrstuvwxyz"
405                 + "{|}~???????????????????????"
406                 + "??????????\u00A0¡¢£¤¥¦§¨©ª«¬\u00AD®¯"
407                 + "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"
408                 + "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîï"
409                 + "ðñòóôõö÷øùúûüýþÿ",
410             new String(bytes, StandardCharsets.ISO_8859_1));
411     }
412 
413     @Test
414     public void testUnicodeFiltering() throws Exception {
415         // Various languages
416         Assert.assertEquals("?????", new String(asByteArray("буквы".toCharArray()), StandardCharsets.ISO_8859_1));
417         Assert.assertEquals("????", new String(asByteArray("四字熟語".toCharArray()), StandardCharsets.ISO_8859_1));
418 
419         // Unicode snowman
420         Assert.assertEquals("?", new String(asByteArray("☃".toCharArray()), StandardCharsets.ISO_8859_1));
421 
422         // Emoji (surrogate pair)
423         Assert.assertEquals("??", new String(asByteArray("\uD83D\uDE00".toCharArray()), StandardCharsets.ISO_8859_1));
424     }
425 
426     private static byte[] asByteArray(final char[] chars) {
427         final ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(chars.length);
428         byteArrayBuffer.append(chars, 0, chars.length);
429         return byteArrayBuffer.toByteArray();
430     }
431 }