Coverage Report - org.apache.commons.codec.binary.BaseNCodecInputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseNCodecInputStream
93%
44/47
97%
33/34
4
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.codec.binary;
 19  
 
 20  
 import static org.apache.commons.codec.binary.BaseNCodec.EOF;
 21  
 
 22  
 import java.io.FilterInputStream;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 
 26  
 import org.apache.commons.codec.binary.BaseNCodec.Context;
 27  
 
 28  
 /**
 29  
  * Abstract superclass for Base-N input streams.
 30  
  *
 31  
  * @since 1.5
 32  
  * @version $Id$
 33  
  */
 34  
 public class BaseNCodecInputStream extends FilterInputStream {
 35  
 
 36  
     private final BaseNCodec baseNCodec;
 37  
 
 38  
     private final boolean doEncode;
 39  
 
 40  13755
     private final byte[] singleByte = new byte[1];
 41  
 
 42  13755
     private final Context context = new Context();
 43  
 
 44  
     protected BaseNCodecInputStream(final InputStream in, final BaseNCodec baseNCodec, final boolean doEncode) {
 45  13755
         super(in);
 46  13755
         this.doEncode = doEncode;
 47  13755
         this.baseNCodec = baseNCodec;
 48  13755
     }
 49  
 
 50  
     /**
 51  
      * {@inheritDoc}
 52  
      *
 53  
      * @return <code>0</code> if the {@link InputStream} has reached <code>EOF</code>,
 54  
      * <code>1</code> otherwise
 55  
      * @since 1.7
 56  
      */
 57  
     @Override
 58  
     public int available() throws IOException {
 59  
         // Note: the logic is similar to the InflaterInputStream:
 60  
         //       as long as we have not reached EOF, indicate that there is more
 61  
         //       data available. As we do not know for sure how much data is left,
 62  
         //       just return 1 as a safe guess.
 63  
 
 64  7
         return context.eof ? 0 : 1;
 65  
     }
 66  
 
 67  
     /**
 68  
      * Marks the current position in this input stream.
 69  
      * <p>The {@link #mark} method of {@link BaseNCodecInputStream} does nothing.</p>
 70  
      *
 71  
      * @param readLimit the maximum limit of bytes that can be read before the mark position becomes invalid.
 72  
      * @since 1.7
 73  
      */
 74  
     @Override
 75  
     public synchronized void mark(final int readLimit) {
 76  0
     }
 77  
 
 78  
     /**
 79  
      * {@inheritDoc}
 80  
      *
 81  
      * @return always returns <code>false</code>
 82  
      */
 83  
     @Override
 84  
     public boolean markSupported() {
 85  2
         return false; // not an easy job to support marks
 86  
     }
 87  
 
 88  
     /**
 89  
      * Reads one <code>byte</code> from this input stream.
 90  
      *
 91  
      * @return the byte as an integer in the range 0 to 255. Returns -1 if EOF has been reached.
 92  
      * @throws IOException
 93  
      *             if an I/O error occurs.
 94  
      */
 95  
     @Override
 96  
     public int read() throws IOException {
 97  89938
         int r = read(singleByte, 0, 1);
 98  89938
         while (r == 0) {
 99  0
             r = read(singleByte, 0, 1);
 100  
         }
 101  89938
         if (r > 0) {
 102  86175
             final byte b = singleByte[0];
 103  86175
             return b < 0 ? 256 + b : b;
 104  
         }
 105  3763
         return EOF;
 106  
     }
 107  
 
 108  
     /**
 109  
      * Attempts to read <code>len</code> bytes into the specified <code>b</code> array starting at <code>offset</code>
 110  
      * from this InputStream.
 111  
      *
 112  
      * @param b
 113  
      *            destination byte array
 114  
      * @param offset
 115  
      *            where to start writing the bytes
 116  
      * @param len
 117  
      *            maximum number of bytes to read
 118  
      *
 119  
      * @return number of bytes read
 120  
      * @throws IOException
 121  
      *             if an I/O error occurs.
 122  
      * @throws NullPointerException
 123  
      *             if the byte array parameter is null
 124  
      * @throws IndexOutOfBoundsException
 125  
      *             if offset, len or buffer size are invalid
 126  
      */
 127  
     @Override
 128  
     public int read(final byte b[], final int offset, final int len) throws IOException {
 129  186618
         if (b == null) {
 130  2
             throw new NullPointerException();
 131  186616
         } else if (offset < 0 || len < 0) {
 132  4
             throw new IndexOutOfBoundsException();
 133  186612
         } else if (offset > b.length || offset + len > b.length) {
 134  4
             throw new IndexOutOfBoundsException();
 135  186608
         } else if (len == 0) {
 136  2
             return 0;
 137  
         } else {
 138  186606
             int readLen = 0;
 139  
             /*
 140  
              Rationale for while-loop on (readLen == 0):
 141  
              -----
 142  
              Base32.readResults() usually returns > 0 or EOF (-1).  In the
 143  
              rare case where it returns 0, we just keep trying.
 144  
 
 145  
              This is essentially an undocumented contract for InputStream
 146  
              implementors that want their code to work properly with
 147  
              java.io.InputStreamReader, since the latter hates it when
 148  
              InputStream.read(byte[]) returns a zero.  Unfortunately our
 149  
              readResults() call must return 0 if a large amount of the data
 150  
              being decoded was non-base32, so this while-loop enables proper
 151  
              interop with InputStreamReader for that scenario.
 152  
              -----
 153  
              This is a fix for CODEC-101
 154  
             */
 155  377407
             while (readLen == 0) {
 156  190801
                 if (!baseNCodec.hasData(context)) {
 157  102583
                     final byte[] buf = new byte[doEncode ? 4096 : 8192];
 158  102583
                     final int c = in.read(buf);
 159  102583
                     if (doEncode) {
 160  53584
                         baseNCodec.encode(buf, 0, c, context);
 161  
                     } else {
 162  48999
                         baseNCodec.decode(buf, 0, c, context);
 163  
                     }
 164  
                 }
 165  190801
                 readLen = baseNCodec.readResults(b, offset, len, context);
 166  
             }
 167  186606
             return readLen;
 168  
         }
 169  
     }
 170  
 
 171  
     /**
 172  
      * Repositions this stream to the position at the time the mark method was last called on this input stream.
 173  
      * <p>
 174  
      * The {@link #reset} method of {@link BaseNCodecInputStream} does nothing except throw an {@link IOException}.
 175  
      *
 176  
      * @throws IOException if this method is invoked
 177  
      * @since 1.7
 178  
      */
 179  
     @Override
 180  
     public synchronized void reset() throws IOException {
 181  0
         throw new IOException("mark/reset not supported");
 182  
     }
 183  
 
 184  
     /**
 185  
      * {@inheritDoc}
 186  
      *
 187  
      * @throws IllegalArgumentException if the provided skip length is negative
 188  
      * @since 1.7
 189  
      */
 190  
     @Override
 191  
     public long skip(final long n) throws IOException {
 192  14
         if (n < 0) {
 193  2
             throw new IllegalArgumentException("Negative skip length: " + n);
 194  
         }
 195  
 
 196  
         // skip in chunks of 512 bytes
 197  12
         final byte[] b = new byte[512];
 198  12
         long todo = n;
 199  
 
 200  22
         while (todo > 0) {
 201  16
             int len = (int) Math.min(b.length, todo);
 202  16
             len = this.read(b, 0, len);
 203  16
             if (len == EOF) {
 204  6
                 break;
 205  
             }
 206  10
             todo -= len;
 207  10
         }
 208  
 
 209  12
         return n - todo;
 210  
     }
 211  
 }