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.jupiter.api.Assertions;
38  import org.junit.jupiter.api.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          Assertions.assertEquals(16, buffer.capacity());
50          Assertions.assertEquals(0, buffer.length());
51          Assertions.assertNotNull(buffer.array());
52          Assertions.assertEquals(16, buffer.array().length);
53          Assertions.assertThrows(IllegalArgumentException.class, () -> new ByteArrayBuffer(-1));
54      }
55  
56      @Test
57      public void testSimpleAppend() throws Exception {
58          final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
59          Assertions.assertEquals(16, buffer.capacity());
60          Assertions.assertEquals(0, buffer.length());
61          final byte[] b1 = buffer.toByteArray();
62          Assertions.assertNotNull(b1);
63          Assertions.assertEquals(0, b1.length);
64          Assertions.assertTrue(buffer.isEmpty());
65          Assertions.assertFalse(buffer.isFull());
66  
67          final byte[] tmp = new byte[] { 1, 2, 3, 4};
68          buffer.append(tmp, 0, tmp.length);
69          Assertions.assertEquals(16, buffer.capacity());
70          Assertions.assertEquals(4, buffer.length());
71          Assertions.assertFalse(buffer.isEmpty());
72          Assertions.assertFalse(buffer.isFull());
73  
74          final byte[] b2 = buffer.toByteArray();
75          Assertions.assertNotNull(b2);
76          Assertions.assertEquals(4, b2.length);
77          for (int i = 0; i < tmp.length; i++) {
78              Assertions.assertEquals(tmp[i], b2[i]);
79              Assertions.assertEquals(tmp[i], buffer.byteAt(i));
80          }
81          buffer.clear();
82          Assertions.assertEquals(16, buffer.capacity());
83          Assertions.assertEquals(0, buffer.length());
84          Assertions.assertTrue(buffer.isEmpty());
85          Assertions.assertFalse(buffer.isFull());
86      }
87  
88      @Test
89      public void testExpandAppend() throws Exception {
90          final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
91          Assertions.assertEquals(4, buffer.capacity());
92  
93          final byte[] tmp = new byte[] { 1, 2, 3, 4};
94          buffer.append(tmp, 0, 2);
95          buffer.append(tmp, 0, 4);
96          buffer.append(tmp, 0, 0);
97  
98          Assertions.assertEquals(8, buffer.capacity());
99          Assertions.assertEquals(6, buffer.length());
100 
101         buffer.append(tmp, 0, 4);
102 
103         Assertions.assertEquals(16, buffer.capacity());
104         Assertions.assertEquals(10, buffer.length());
105     }
106 
107     @Test
108     public void testAppendHeapByteBuffer() {
109         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
110         Assertions.assertEquals(4, buffer.capacity());
111 
112         final ByteBuffer tmp = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6});
113         buffer.append(tmp);
114 
115         Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained");
116         Assertions.assertEquals(8, buffer.capacity());
117         Assertions.assertEquals(6, buffer.length());
118 
119         tmp.clear();
120         buffer.append(tmp);
121 
122         Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained");
123         Assertions.assertEquals(16, buffer.capacity());
124         Assertions.assertEquals(12, buffer.length());
125         Assertions.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray());
126     }
127 
128     @Test
129     public void testAppendHeapByteBufferWithOffset() {
130         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
131         Assertions.assertEquals(4, buffer.capacity());
132 
133         final ByteBuffer tmp = ByteBuffer.wrap(new byte[] { 7, 7, 1, 2, 3, 4, 5, 6, 7, 7}, 2, 6).slice();
134         Assertions.assertTrue(tmp.arrayOffset() > 0, "Validate this is testing a buffer with an array offset");
135 
136         buffer.append(tmp);
137 
138         Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained");
139         Assertions.assertEquals(8, buffer.capacity());
140         Assertions.assertEquals(6, buffer.length());
141 
142         tmp.clear();
143         Assertions.assertEquals(6, tmp.remaining());
144         buffer.append(tmp);
145 
146         Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained");
147         Assertions.assertEquals(16, buffer.capacity());
148         Assertions.assertEquals(12, buffer.length());
149         Assertions.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray());
150     }
151 
152     @Test
153     public void testAppendDirectByteBuffer() {
154         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
155         Assertions.assertEquals(4, buffer.capacity());
156 
157         final ByteBuffer tmp = ByteBuffer.allocateDirect(6);
158         tmp.put(new byte[] { 1, 2, 3, 4, 5, 6}).flip();
159         buffer.append(tmp);
160 
161         Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained");
162         Assertions.assertEquals(8, buffer.capacity());
163         Assertions.assertEquals(6, buffer.length());
164 
165         tmp.clear();
166         buffer.append(tmp);
167 
168         Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained");
169         Assertions.assertEquals(16, buffer.capacity());
170         Assertions.assertEquals(12, buffer.length());
171         Assertions.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray());
172     }
173 
174     @Test
175     public void testInvalidAppend() throws Exception {
176         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
177         buffer.append((byte[])null, 0, 0);
178 
179         final byte[] tmp = new byte[] { 1, 2, 3, 4};
180         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0));
181         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1));
182         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8));
183         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE));
184         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4));
185     }
186 
187     @Test
188     public void testAppendOneByte() throws Exception {
189         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
190         Assertions.assertEquals(4, buffer.capacity());
191 
192         final byte[] tmp = new byte[] { 1, 127, -1, -128, 1, -2};
193         for (final byte element : tmp) {
194             buffer.append(element);
195         }
196         Assertions.assertEquals(8, buffer.capacity());
197         Assertions.assertEquals(6, buffer.length());
198 
199         for (int i = 0; i < tmp.length; i++) {
200             Assertions.assertEquals(tmp[i], buffer.byteAt(i));
201         }
202     }
203 
204     @Test
205     public void testSetLength() throws Exception {
206         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
207         buffer.setLength(2);
208         Assertions.assertEquals(2, buffer.length());
209     }
210 
211     @Test
212     public void testSetInvalidLength() throws Exception {
213         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
214         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setLength(-2));
215         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setLength(200));
216     }
217 
218     @Test
219     public void testEnsureCapacity() throws Exception {
220         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
221         buffer.ensureCapacity(2);
222         Assertions.assertEquals(4, buffer.capacity());
223         buffer.ensureCapacity(8);
224         Assertions.assertEquals(8, buffer.capacity());
225     }
226 
227     @Test
228     public void testIndexOf() throws Exception {
229         final byte COLON = (byte) ':';
230         final byte COMMA = (byte) ',';
231         final byte[] bytes = "name1: value1; name2: value2".getBytes(StandardCharsets.US_ASCII);
232         final int index1 = 5;
233         final int index2 = 20;
234 
235         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
236         buffer.append(bytes, 0, bytes.length);
237 
238         Assertions.assertEquals(index1, buffer.indexOf(COLON));
239         Assertions.assertEquals(-1, buffer.indexOf(COMMA));
240         Assertions.assertEquals(index1, buffer.indexOf(COLON, -1, 11));
241         Assertions.assertEquals(index1, buffer.indexOf(COLON, 0, 1000));
242         Assertions.assertEquals(-1, buffer.indexOf(COLON, 2, 1));
243         Assertions.assertEquals(index2, buffer.indexOf(COLON, index1 + 1, buffer.length()));
244     }
245 
246     @Test
247     public void testAppendCharArrayAsAscii() throws Exception {
248         final String s1 = "stuff";
249         final String s2 = " and more stuff";
250         final char[] b1 = s1.toCharArray();
251         final char[] b2 = s2.toCharArray();
252 
253         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
254         buffer.append(b1, 0, b1.length);
255         buffer.append(b2, 0, b2.length);
256 
257         Assertions.assertEquals(s1 + s2, new String(buffer.toByteArray(), StandardCharsets.US_ASCII));
258     }
259 
260     @Test
261     public void testAppendNullCharArray() throws Exception {
262         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
263         buffer.append((char[])null, 0, 0);
264         Assertions.assertEquals(0, buffer.length());
265     }
266 
267     @Test
268     public void testAppendEmptyCharArray() throws Exception {
269         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
270         buffer.append(new char[] {}, 0, 0);
271         Assertions.assertEquals(0, buffer.length());
272     }
273 
274     @Test
275     public void testAppendNullCharArrayBuffer() throws Exception {
276         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
277         buffer.append((CharArrayBuffer)null, 0, 0);
278         Assertions.assertEquals(0, buffer.length());
279     }
280 
281     @Test
282     public void testAppendNullByteBuffer() throws Exception {
283         final ByteArrayBuffer buffer = new ByteArrayBuffer(8);
284         final ByteBuffer nullBuffer = null;
285         buffer.append(nullBuffer);
286         Assertions.assertEquals(0, buffer.length());
287     }
288 
289     @Test
290     public void testInvalidAppendCharArrayAsAscii() throws Exception {
291         final ByteArrayBuffer buffer = new ByteArrayBuffer(4);
292         buffer.append((char[])null, 0, 0);
293 
294         final char[] tmp = new char[] { '1', '2', '3', '4'};
295         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0));
296         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1));
297         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8));
298         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE));
299         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4));
300     }
301 
302     @Test
303     public void testSerialization() throws Exception {
304         final ByteArrayBuffer orig = new ByteArrayBuffer(32);
305         orig.append(1);
306         orig.append(2);
307         orig.append(3);
308         final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream();
309         try (final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer)) {
310             outStream.writeObject(orig);
311         }
312         final byte[] raw = outbuffer.toByteArray();
313         final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw);
314         final ObjectInputStream inStream = new ObjectInputStream(inBuffer);
315         final ByteArrayBuffer clone = (ByteArrayBuffer) inStream.readObject();
316         Assertions.assertEquals(orig.capacity(), clone.capacity());
317         Assertions.assertEquals(orig.length(), clone.length());
318         final byte[] data = clone.toByteArray();
319         Assertions.assertNotNull(data);
320         Assertions.assertEquals(3, data.length);
321         Assertions.assertEquals(1, data[0]);
322         Assertions.assertEquals(2, data[1]);
323         Assertions.assertEquals(3, data[2]);
324     }
325 
326     @Test
327     public void testControlCharFiltering() throws Exception {
328         final char[] chars = new char[256];
329         for (char i = 0; i < 256; i++) {
330             chars[i] = i;
331         }
332 
333         final byte[] bytes = asByteArray(chars);
334 
335         Assertions.assertEquals(
336             "?????????\t??????????????????????"
337                 + " !\"#$%&'()*+,-./0123456789:;<=>?"
338                 + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
339                 + "`abcdefghijklmnopqrstuvwxyz"
340                 + "{|}~???????????????????????"
341                 + "??????????\u00A0¡¢£¤¥¦§¨©ª«¬\u00AD®¯"
342                 + "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"
343                 + "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîï"
344                 + "ðñòóôõö÷øùúûüýþÿ",
345             new String(bytes, StandardCharsets.ISO_8859_1));
346     }
347 
348     @Test
349     public void testUnicodeFiltering() throws Exception {
350         // Various languages
351         Assertions.assertEquals("?????", new String(asByteArray("буквы".toCharArray()), StandardCharsets.ISO_8859_1));
352         Assertions.assertEquals("????", new String(asByteArray("四字熟語".toCharArray()), StandardCharsets.ISO_8859_1));
353 
354         // Unicode snowman
355         Assertions.assertEquals("?", new String(asByteArray("☃".toCharArray()), StandardCharsets.ISO_8859_1));
356 
357         // Emoji (surrogate pair)
358         Assertions.assertEquals("??", new String(asByteArray("\uD83D\uDE00".toCharArray()), StandardCharsets.ISO_8859_1));
359     }
360 
361     private static byte[] asByteArray(final char[] chars) {
362         final ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(chars.length);
363         byteArrayBuffer.append(chars, 0, chars.length);
364         return byteArrayBuffer.toByteArray();
365     }
366 }