001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.util.byteaccess;
021
022import static org.easymock.EasyMock.createStrictControl;
023import static org.junit.Assert.assertEquals;
024
025import java.nio.ByteOrder;
026import java.util.ArrayList;
027import java.util.List;
028
029import org.apache.mina.core.buffer.IoBuffer;
030import org.apache.mina.util.byteaccess.ByteArray.Cursor;
031import org.apache.mina.util.byteaccess.CompositeByteArray.CursorListener;
032import org.apache.mina.util.byteaccess.CompositeByteArrayRelativeWriter.ChunkedExpander;
033import org.apache.mina.util.byteaccess.CompositeByteArrayRelativeWriter.Flusher;
034import org.easymock.IMocksControl;
035import org.junit.Test;
036
037/**
038 * Tests classes in the <code>byteaccess</code> package.
039 * 
040 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
041 */
042public class ByteAccessTest {
043
044    private List<String> operations = new ArrayList<String>();
045
046    private void resetOperations() {
047        operations.clear();
048    }
049
050    private void assertOperationCountEquals(int expectedCount) {
051        assertEquals("Operations: " + operations, expectedCount, operations.size());
052    }
053
054    private void addOperation(String description) {
055        operations.add(description);
056    }
057
058    @Test
059    public void testBufferByteArray() throws Exception {
060        ByteArray ba = getByteArrayFactory().create(1000);
061        testAbsoluteReaderAndWriter(0, 1000, ba, ba);
062        testAbsoluteReaderAndWriter(0, 1000, ba, ba);
063        Cursor readCursor = ba.cursor();
064        Cursor writeCursor = ba.cursor();
065        testRelativeReaderAndWriter(1000, readCursor, writeCursor);
066    }
067
068    @Test
069    public void testCompositeAddAndRemove() throws Exception {
070        CompositeByteArray cba = new CompositeByteArray();
071        assertEquals(0, cba.first());
072        assertEquals(0, cba.last());
073        cba.addFirst(getByteArrayFactory().create(100));
074        assertEquals(-100, cba.first());
075        assertEquals(0, cba.last());
076        cba.addFirst(getByteArrayFactory().create(100));
077        assertEquals(-200, cba.first());
078        assertEquals(0, cba.last());
079        cba.addLast(getByteArrayFactory().create(100));
080        assertEquals(-200, cba.first());
081        assertEquals(100, cba.last());
082        cba.removeFirst();
083        assertEquals(-100, cba.first());
084        assertEquals(100, cba.last());
085        cba.addLast(getByteArrayFactory().create(100));
086        assertEquals(-100, cba.first());
087        assertEquals(200, cba.last());
088        cba.removeLast();
089        assertEquals(-100, cba.first());
090        assertEquals(100, cba.last());
091        cba.removeFirst();
092        assertEquals(0, cba.first());
093        assertEquals(100, cba.last());
094        cba.removeFirst();
095        assertEquals(100, cba.first());
096        assertEquals(100, cba.last());
097        cba.addLast(getByteArrayFactory().create(100));
098        assertEquals(100, cba.first());
099        assertEquals(200, cba.last());
100    }
101
102    private BufferByteArray wrapString(String string) {
103        byte[] bytes = string.getBytes();
104        IoBuffer bb = IoBuffer.wrap(bytes);
105        BufferByteArray ba = new BufferByteArray(bb) {
106
107            @Override
108            public void free() {
109                addOperation(this + ".free()");
110                // Nothing to do.
111            }
112
113        };
114        return ba;
115    }
116
117    private String toString(ByteArray ba) {
118        IoBuffer bb = IoBuffer.allocate(ba.length());
119        ba.get(0, bb);
120        byte[] bytes = bb.array();
121        String string = new String(bytes);
122        return string;
123    }
124
125    @Test
126    public void testCompositeStringJoin() throws Exception {
127        ByteArray ba1 = wrapString("Hello");
128        ByteArray ba2 = wrapString("MINA");
129        ByteArray ba3 = wrapString("World");
130
131        CompositeByteArray cba = new CompositeByteArray();
132        cba.addLast(ba1);
133        cba.addLast(ba2);
134        cba.addLast(ba3);
135
136        assertEquals("HelloMINAWorld", toString(cba));
137    }
138
139    @Test
140    public void testCompositeCursor() throws Exception {
141        IMocksControl mc = createStrictControl();
142
143        ByteArray ba1 = getByteArrayFactory().create(10);
144        ByteArray ba2 = getByteArrayFactory().create(10);
145        ByteArray ba3 = getByteArrayFactory().create(10);
146
147        CompositeByteArray cba = new CompositeByteArray();
148        cba.addLast(ba1);
149        cba.addLast(ba2);
150        cba.addLast(ba3);
151
152        CursorListener cl = mc.createMock(CursorListener.class);
153
154        mc.reset();
155        mc.replay();
156        Cursor cursor = cba.cursor(cl);
157        mc.verify();
158
159        mc.reset();
160        cl.enteredFirstComponent(0, ba1);
161        mc.replay();
162        cursor.get();
163        mc.verify();
164
165        mc.reset();
166        mc.replay();
167        cursor.setIndex(10);
168        mc.verify();
169
170        mc.reset();
171        cl.enteredNextComponent(10, ba2);
172        mc.replay();
173        cursor.put((byte) 55);
174        mc.verify();
175
176        mc.reset();
177        mc.replay();
178        cursor.setIndex(9);
179        mc.verify();
180
181        mc.reset();
182        cl.enteredPreviousComponent(0, ba1);
183        cl.enteredNextComponent(10, ba2);
184        mc.replay();
185        cursor.putInt(66);
186        mc.verify();
187
188        mc.reset();
189        cl.enteredNextComponent(20, ba3);
190        mc.replay();
191        cursor.setIndex(29);
192        cursor.get();
193        mc.verify();
194
195        cba.removeLast(); // Force cursor to relocate itself.
196
197        mc.reset();
198        cl.enteredLastComponent(10, ba2);
199        mc.replay();
200        cursor.setIndex(15);
201        cursor.get();
202        mc.verify();
203
204        mc.reset();
205        cl.enteredPreviousComponent(0, ba1);
206        mc.replay();
207        cursor.setIndex(0);
208        cursor.get();
209        mc.verify();
210    }
211
212    @Test
213    public void testCompositeByteArray() throws Exception {
214        CompositeByteArray ba = new CompositeByteArray();
215        for (int i = 0; i < 1000; i += 100) {
216            ba.addLast(getByteArrayFactory().create(100));
217        }
218        resetOperations();
219        testAbsoluteReaderAndWriter(0, 1000, ba, ba);
220        testAbsoluteReaderAndWriter(0, 1000, ba, ba);
221        assertOperationCountEquals(0);
222        Cursor readCursor = ba.cursor();
223        Cursor writeCursor = ba.cursor();
224        testRelativeReaderAndWriter(1000, readCursor, writeCursor);
225        assertOperationCountEquals(0);
226    }
227
228    @Test
229    public void testCompositeByteArrayRelativeReaderAndWriter() throws Exception {
230        CompositeByteArray cba = new CompositeByteArray();
231        CompositeByteArrayRelativeReader cbarr = new CompositeByteArrayRelativeReader(cba, true);
232        CompositeByteArrayRelativeWriter cbarw = new CompositeByteArrayRelativeWriter(cba, getExpander(100),
233                getFlusher(), false);
234        resetOperations();
235        testRelativeReaderAndWriter(10, cbarr, cbarw);
236        assertOperationCountEquals(2);
237        resetOperations();
238        testRelativeReaderAndWriter(100, cbarr, cbarw);
239        assertOperationCountEquals(3);
240        resetOperations();
241        testRelativeReaderAndWriter(1000, cbarr, cbarw);
242        assertOperationCountEquals(30);
243        resetOperations();
244        testRelativeReaderAndWriter(10000, cbarr, cbarw);
245        assertOperationCountEquals(300);
246        resetOperations();
247        testRelativeReaderAndWriter(90, cbarr, cbarw);
248        assertOperationCountEquals(0); // Last free doesn't occur, since cursor only moves lazily.
249    }
250
251    @Test
252    public void testCompositeByteArrayRelativeReaderAndWriterWithFlush() throws Exception {
253        CompositeByteArray cba = new CompositeByteArray();
254        CompositeByteArrayRelativeReader cbarr = new CompositeByteArrayRelativeReader(cba, true);
255        CompositeByteArrayRelativeWriter cbarw = new CompositeByteArrayRelativeWriter(cba, getExpander(100),
256                getFlusher(), true);
257        resetOperations();
258        testRelativeReaderAndWriter(10, cbarr, cbarw);
259        assertOperationCountEquals(2);
260        resetOperations();
261        testRelativeReaderAndWriter(100, cbarr, cbarw);
262        assertOperationCountEquals(4);
263        resetOperations();
264        testRelativeReaderAndWriter(1000, cbarr, cbarw);
265        assertOperationCountEquals(40);
266        resetOperations();
267        testRelativeReaderAndWriter(10000, cbarr, cbarw);
268        assertOperationCountEquals(400);
269        resetOperations();
270        testRelativeReaderAndWriter(90, cbarr, cbarw);
271        assertOperationCountEquals(0); // Last free doesn't occur, since cursor only moves lazily.
272    }
273
274    @Test
275    public void testCompositeRemoveTo() throws Exception {
276        CompositeByteArray cba = new CompositeByteArray();
277        {
278            // Remove nothing.
279            resetOperations();
280            ByteArray removed = cba.removeTo(0);
281            assertEquals(0, removed.first());
282            assertEquals(0, removed.last());
283            assertEquals(0, cba.first());
284            assertEquals(0, cba.last());
285            removed.free();
286            assertOperationCountEquals(0);
287        }
288        cba.addLast(getByteArrayFactory().create(100));
289        {
290            // Remove nothing.
291            resetOperations();
292            ByteArray removed = cba.removeTo(0);
293            assertEquals(0, removed.first());
294            assertEquals(0, removed.last());
295            assertEquals(0, cba.first());
296            assertEquals(100, cba.last());
297            removed.free();
298            assertOperationCountEquals(0);
299        }
300        {
301            // Remove entire component.
302            resetOperations();
303            ByteArray removed = cba.removeTo(100);
304            assertEquals(0, removed.first());
305            assertEquals(100, removed.last());
306            assertEquals(100, cba.first());
307            assertEquals(100, cba.last());
308            removed.free();
309            assertOperationCountEquals(1);
310        }
311        {
312            // Remove nothing.
313            resetOperations();
314            ByteArray removed = cba.removeTo(100);
315            assertEquals(0, removed.first());
316            assertEquals(0, removed.last());
317            assertEquals(100, cba.first());
318            assertEquals(100, cba.last());
319            removed.free();
320            assertOperationCountEquals(0);
321        }
322        cba.addLast(getByteArrayFactory().create(100));
323        {
324            // Remove nothing.
325            resetOperations();
326            ByteArray removed = cba.removeTo(100);
327            assertEquals(0, removed.first());
328            assertEquals(0, removed.last());
329            assertEquals(100, cba.first());
330            assertEquals(200, cba.last());
331            removed.free();
332            assertOperationCountEquals(0);
333        }
334        {
335            // Remove half a component.
336            resetOperations();
337            ByteArray removed = cba.removeTo(150);
338            assertEquals(0, removed.first());
339            assertEquals(50, removed.last());
340            assertEquals(150, cba.first());
341            assertEquals(200, cba.last());
342            removed.free();
343            assertOperationCountEquals(0); // Doesn't free until component finished.
344        }
345        {
346            // Remove nothing.
347            resetOperations();
348            ByteArray removed = cba.removeTo(150);
349            assertEquals(0, removed.first());
350            assertEquals(0, removed.last());
351            assertEquals(150, cba.first());
352            assertEquals(200, cba.last());
353            removed.free();
354            assertOperationCountEquals(0);
355        }
356        {
357            // Remove other half.
358            resetOperations();
359            ByteArray removed = cba.removeTo(200);
360            assertEquals(0, removed.first());
361            assertEquals(50, removed.last());
362            assertEquals(200, cba.first());
363            assertEquals(200, cba.last());
364            removed.free();
365            assertOperationCountEquals(1); // Frees ByteArray behind both buffers.
366        }
367    }
368
369    @Test
370    public void testCompositeByteArraySlicing() {
371        CompositeByteArray cba = new CompositeByteArray();
372        cba.addLast(getByteArrayFactory().create(10));
373        cba.addLast(getByteArrayFactory().create(10));
374        cba.addLast(getByteArrayFactory().create(10));
375        testByteArraySlicing(cba, 0, 30);
376        testByteArraySlicing(cba, 5, 10);
377        testByteArraySlicing(cba, 10, 20);
378        testByteArraySlicing(cba, 1, 28);
379        testByteArraySlicing(cba, 19, 2);
380    }
381
382    @Test
383    public void testBufferByteArraySlicing() {
384        ByteArray bba = getByteArrayFactory().create(30);
385        testByteArraySlicing(bba, 0, 30);
386        testByteArraySlicing(bba, 5, 10);
387        testByteArraySlicing(bba, 10, 20);
388        testByteArraySlicing(bba, 1, 28);
389        testByteArraySlicing(bba, 19, 2);
390
391    }
392
393    private void testByteArraySlicing(ByteArray ba, int start, int length) {
394        ByteArray slice = ba.slice(start, length);
395        for (int i = 0; i < length; i++) {
396            byte b1 = (byte) (i % 67);
397            byte b2 = (byte) (i % 36);
398            int sourceIndex = i + start;
399            int sliceIndex = i;
400            ba.put(sourceIndex, b1);
401            assertEquals(b1, ba.get(sourceIndex));
402            assertEquals(b1, slice.get(sliceIndex));
403            slice.put(sliceIndex, b2);
404            assertEquals(b2, ba.get(sourceIndex));
405            assertEquals(b2, slice.get(sliceIndex));
406        }
407    }
408
409    private ChunkedExpander getExpander(final int chunkSize) {
410        return new ChunkedExpander(getByteArrayFactory(), chunkSize) {
411            @Override
412            public void expand(CompositeByteArray cba, int minSize) {
413                addOperation("ChunkedExpander(" + chunkSize + ").expand(" + cba + "," + minSize + ")");
414                super.expand(cba, minSize);
415            }
416        };
417    }
418
419    private Flusher getFlusher() {
420        return new CompositeByteArrayRelativeWriter.Flusher() {
421
422            public void flush(ByteArray ba) {
423                addOperation("Flusher().flush(" + ba + ")");
424                ba.free();
425            }
426
427        };
428    }
429
430    private SimpleByteArrayFactory getByteArrayFactory() {
431        return new SimpleByteArrayFactory() {
432            @Override
433            public ByteArray create(final int size) {
434                if (size < 0) {
435                    throw new IllegalArgumentException("Buffer size must not be negative:" + size);
436                }
437                IoBuffer bb = IoBuffer.allocate(size);
438                ByteArray ba = new BufferByteArray(bb) {
439
440                    @Override
441                    public void free() {
442                        addOperation(this + ".free()");
443                        // Nothing to do.
444                    }
445
446                };
447                addOperation("SimpleByteArrayFactory().create(" + size + ") = " + ba);
448                return ba;
449            }
450        };
451    }
452
453    private void testRelativeReaderAndWriter(int length, IoRelativeReader reader, IoRelativeWriter writer) {
454        for (int i = 0; i < length; i++) {
455            byte b = (byte) (i % 67);
456            writer.put(b);
457            assertEquals(b, reader.get());
458        }
459    }
460
461    private void testAbsoluteReaderAndWriter(int start, int length, IoAbsoluteReader reader, IoAbsoluteWriter writer) {
462        for (int i = start; i < length; i++) {
463            byte b = (byte) (i % 67);
464            writer.put(i, b);
465            assertEquals(b, reader.get(i));
466        }
467    }
468
469    @Test
470    public void testByteArrayPrimitiveAccess() {
471        ByteArray bbaBig = getByteArrayFactory().create(1000);
472        bbaBig.order(ByteOrder.BIG_ENDIAN);
473        testPrimitiveAccess(bbaBig.cursor(), bbaBig.cursor());
474
475        ByteArray bbaLittle = getByteArrayFactory().create(1000);
476        bbaLittle.order(ByteOrder.LITTLE_ENDIAN);
477        testPrimitiveAccess(bbaLittle.cursor(), bbaLittle.cursor());
478    }
479
480    @Test
481    public void testByteArrayBufferAccess() {
482        ByteArray ba = getByteArrayFactory().create(1);
483        ba.put(0, (byte) 99);
484        IoBuffer bb = IoBuffer.allocate(2);
485
486        bb.clear();
487        Cursor cursor = ba.cursor();
488        assertEquals(0, cursor.getIndex());
489        assertEquals(1, cursor.getRemaining());
490        assertEquals(0, bb.position());
491        assertEquals(2, bb.remaining());
492        cursor.get(bb);
493        assertEquals(1, cursor.getIndex());
494        assertEquals(0, cursor.getRemaining());
495        assertEquals(1, bb.position());
496        assertEquals(1, bb.remaining());
497    }
498
499    @Test
500    public void testCompositeByteArrayPrimitiveAccess() {
501        CompositeByteArray cbaBig = new CompositeByteArray();
502        cbaBig.order(ByteOrder.BIG_ENDIAN);
503        for (int i = 0; i < 1000; i++) {
504            ByteArray component = getByteArrayFactory().create(1);
505            component.order(ByteOrder.BIG_ENDIAN);
506            cbaBig.addLast(component);
507        }
508        testPrimitiveAccess(cbaBig.cursor(), cbaBig.cursor());
509
510        CompositeByteArray cbaLittle = new CompositeByteArray();
511        cbaLittle.order(ByteOrder.LITTLE_ENDIAN);
512        for (int i = 0; i < 1000; i++) {
513            ByteArray component = getByteArrayFactory().create(1);
514            component.order(ByteOrder.LITTLE_ENDIAN);
515            cbaLittle.addLast(component);
516        }
517        testPrimitiveAccess(cbaLittle.cursor(), cbaLittle.cursor());
518    }
519
520    @Test
521    public void testCompositeByteArrayWrapperPrimitiveAccess() {
522        CompositeByteArray cbaBig = new CompositeByteArray();
523        cbaBig.order(ByteOrder.BIG_ENDIAN);
524        for (int i = 0; i < 1000; i++) {
525            ByteArray component = getByteArrayFactory().create(1);
526            component.order(ByteOrder.BIG_ENDIAN);
527            cbaBig.addLast(component);
528        }
529        testPrimitiveAccess(new CompositeByteArrayRelativeWriter(cbaBig, getExpander(10), getFlusher(), false),
530                new CompositeByteArrayRelativeReader(cbaBig, true));
531
532        CompositeByteArray cbaLittle = new CompositeByteArray();
533        cbaLittle.order(ByteOrder.LITTLE_ENDIAN);
534        for (int i = 0; i < 1000; i++) {
535            ByteArray component = getByteArrayFactory().create(1);
536            component.order(ByteOrder.LITTLE_ENDIAN);
537            cbaLittle.addLast(component);
538        }
539        testPrimitiveAccess(new CompositeByteArrayRelativeWriter(cbaLittle, getExpander(10), getFlusher(), false),
540                new CompositeByteArrayRelativeReader(cbaLittle, true));
541    }
542
543    private void testPrimitiveAccess(IoRelativeWriter write, IoRelativeReader read) {
544        byte b = (byte) 0x12;
545        write.put(b);
546        assertEquals(b, read.get());
547
548        short s = (short) 0x12;
549        write.putShort(s);
550        assertEquals(s, read.getShort());
551
552        int i = 0x12345678;
553        write.putInt(i);
554        assertEquals(i, read.getInt());
555
556        long l = 0x1234567890123456L;
557        write.putLong(l);
558        assertEquals(l, read.getLong());
559
560        float f = Float.intBitsToFloat(i);
561        write.putFloat(f);
562        assertEquals(f, read.getFloat(), 0);
563
564        double d = Double.longBitsToDouble(l);
565        write.putDouble(d);
566        assertEquals(d, read.getDouble(), 0);
567
568        char c = (char) 0x1234;
569        write.putChar(c);
570        assertEquals(c, read.getChar());
571    }
572
573}