1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.crypto.internal.encoder

File BcBinaryStringEncoderInputStream.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

48
112
17
2
352
214
45
0.4
6.59
8.5
2.65

Classes

Class Line # Actions
BcBinaryStringEncoderInputStream 33 46 0% 15 16
0.771428677.1%
BcBinaryStringEncoderInputStream.InputBuffer 59 66 0% 30 13
0.878504787.9%
 

Contributing tests

This file is covered by 12 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.crypto.internal.encoder;
21   
22    import java.io.ByteArrayOutputStream;
23    import java.io.FilterInputStream;
24    import java.io.IOException;
25    import java.io.InputStream;
26   
27    /**
28    * An input stream wrapper that will decode string data to binary in block.
29    *
30    * @version $Id: 7e14cc3b12f7bfc6f644f063b76859056e0a92ce $
31    * @since 5.4M1
32    */
 
33    class BcBinaryStringEncoderInputStream extends FilterInputStream
34    {
35    /** Number of bytes of binary data that is produced from {@code charSize} chars of encoded data. */
36    private final int blockSize;
37   
38    /** Number of bytes of string data that should be decoded together. */
39    private final int charSize;
40   
41    /** A one byte buffer to optimize one byte writing. */
42    private byte[] oneByte = new byte[1];
43   
44    /** An overflow buffer to collate data read in advance. */
45    private byte[] ofBuf;
46   
47    /** Current offset in byte of valid data in the overflow buffer. */
48    private int ofOff;
49   
50    /** Current length in byte of valid data in the overflow buffer. */
51    private int ofLen;
52   
53    /** Encoder used to decode data. */
54    private final InternalBinaryStringEncoder encoder;
55   
56    /**
57    * Buffer reading input data for a given number of input blocks (including blanks).
58    */
 
59    class InputBuffer
60    {
61    /** Buffer to containing read data. */
62    private byte[] buf;
63   
64    /** Length in bytes of valid data in the buffer. */
65    private int bufLen;
66   
67    /** Count in bytes of blanks chars in the buffer. */
68    private int bcount;
69   
70    /** Count in bytes of blanks chars read during the last read operation. */
71    private int rblank;
72   
73    /**
74    * Construct an new buffer initialized with the amount of data requested, or less if EOF is reach.
75    *
76    * @param blen number of block of input string data to read.
77    * @throws IOException on error.
78    */
 
79  132 toggle InputBuffer(int blen) throws IOException
80    {
81  132 int rlen = 0;
82  132 int rbl = blen * BcBinaryStringEncoderInputStream.this.charSize;
83  132 this.buf = new byte[rbl];
84   
85    // Try to read the number of block requested, read more as needed if some blank char has been read.
86  286 while (rbl > 0 && rlen >= 0) {
87  154 rlen = read(rbl);
88  154 rbl = this.rblank;
89    }
90   
91  132 rlen = this.bufLen - this.bcount;
92   
93    // Some non-blank data found ?
94  132 if (rlen > 0) {
95    // Wait until reaching EOF or being aligned on block size
96    // Remember that some input stream may be lazy,
97    // but should at least return one byte, else EOF is reached.
98  120 int rblen = (rlen + BcBinaryStringEncoderInputStream.this.charSize - 1)
99    / BcBinaryStringEncoderInputStream.this.charSize;
100  120 int runder = (rblen * BcBinaryStringEncoderInputStream.this.charSize) - rlen;
101  120 int rrlen = 0;
102  120 while (runder > 0 && rrlen >= 0) {
103  0 rrlen = read(runder);
104  0 if (rrlen > 0) {
105  0 runder -= rrlen - this.rblank;
106    }
107    }
108    }
109    }
110   
111    /**
112    * @return the data buffer.
113    */
 
114  120 toggle byte[] getBuffer()
115    {
116  120 return this.buf;
117    }
118   
119    /**
120    * @return the length in bytes of valid data in the buffer.
121    */
 
122  120 toggle int getReadLength()
123    {
124  120 return this.bufLen;
125    }
126   
127    /**
128    * @return the count in bytes of non-blank data in the buffer.
129    */
 
130  132 toggle int getEffectiveLength()
131    {
132  132 return this.bufLen - this.bcount;
133    }
134   
135    /**
136    * Count blanks char in a given area of the buffer.
137    *
138    * @param off starting offset
139    * @param len length of the area
140    * @return number of bytes containing blank characters.
141    */
 
142  140 toggle private int countBlank(int off, int len)
143    {
144  140 int blank = 0;
145  3150 for (int i = off; i < len; i++) {
146  3010 if (isBlank(this.buf[i])) {
147  22 blank++;
148    }
149    }
150  140 return blank;
151    }
152   
 
153  6040 toggle private boolean isBlank(byte b)
154    {
155  6040 return (b == '\n' || b == '\r' || b == '\t' || b == ' ');
156    }
157   
158    /**
159    * Ensure the buffer is large enough to contains a given amount of bytes.
160    *
161    * @param len the number of bytes the buffer should be able to contains.
162    */
 
163  154 toggle private void ensureSize(int len)
164    {
165  154 if (len > this.buf.length) {
166  20 byte[] nbuf = new byte[len];
167  20 System.arraycopy(this.buf, 0, nbuf, 0, this.buf.length);
168  20 this.buf = nbuf;
169    }
170    }
171   
172    /**
173    * Appends some data from the input stream to valid data in the buffer.
174    *
175    * @param len number of bytes to be read.
176    * @return number of bytes effectively read.
177    * @throws IOException on error.
178    */
 
179  154 toggle private int read(int len) throws IOException
180    {
181  154 ensureSize(this.bufLen + len);
182  154 return readBase64(len);
183    }
184   
185    /**
186    * Append only valid base64 data from the input stream in the buffer.
187    *
188    * The buffer may be affected over len size even if the returned len is lower.
189    *
190    * @param len number of bytes to be read.
191    * @return number of bytes effectively read.
192    * @throws IOException on error.
193    */
 
194  154 toggle private int readBase64(int len) throws IOException
195    {
196  154 int rlen;
197  154 if (BcBinaryStringEncoderInputStream.this.in.markSupported()) {
198  77 rlen = readBase64WithMark(len);
199    } else {
200  77 rlen = readBase64WithoutMark(len);
201    }
202  154 if (rlen > 0) {
203  140 this.rblank = countBlank(this.bufLen, rlen);
204  140 this.bufLen += rlen;
205  140 this.bcount += this.rblank;
206  140 return rlen;
207    }
208  14 return -1;
209    }
210   
 
211  77 toggle private int readBase64WithoutMark(int len) throws IOException
212    {
213  77 int rlen;
214  77 rlen = this.bufLen;
215  1592 while (rlen < (this.bufLen + len)) {
216  1528 int c = BcBinaryStringEncoderInputStream.this.in.read();
217  1528 if (c < 0) {
218  13 break;
219    }
220  1515 byte b = (byte) c;
221  1515 if (!isBlank(b) && !BcBinaryStringEncoderInputStream.this.encoder.isValidEncoding(b)) {
222  0 break;
223    }
224  1515 this.buf[rlen++] = b;
225    }
226  77 rlen -= this.bufLen;
227  77 return rlen;
228    }
229   
 
230  77 toggle private int readBase64WithMark(int len) throws IOException
231    {
232  77 int rlen;
233  77 BcBinaryStringEncoderInputStream.this.in.mark(len);
234  77 rlen = BcBinaryStringEncoderInputStream.this.in.read(this.buf, this.bufLen, len);
235  77 int i = this.bufLen;
236  1592 while (i < (this.bufLen + rlen)) {
237  1515 byte b = this.buf[i];
238  1515 if (!isBlank(b) && !BcBinaryStringEncoderInputStream.this.encoder.isValidEncoding(b)) {
239  0 break;
240    }
241  1515 i++;
242    }
243  77 i -= this.bufLen;
244  77 if (i < rlen) {
245  0 BcBinaryStringEncoderInputStream.this.in.reset();
246  0 rlen = (int) BcBinaryStringEncoderInputStream.this.in.skip(i);
247    }
248  77 return rlen;
249    }
250    }
251   
252    /**
253    * Construct a filter stream for the given encoder, following the specifications.
254    *
255    * @param inputStream the underlying input stream where data will be read.
256    * @param encoder the encoder used to decode data.
257    */
 
258  12 toggle BcBinaryStringEncoderInputStream(InputStream inputStream, InternalBinaryStringEncoder encoder)
259    {
260  12 super(inputStream);
261  12 this.encoder = encoder;
262  12 this.blockSize = encoder.getEncodingBlockSize();
263  12 this.charSize = encoder.getDecodingBlockSize();
264  12 this.ofBuf = new byte[this.blockSize];
265    }
266   
 
267  0 toggle @Override
268    public int read() throws IOException
269    {
270  0 if (read(this.oneByte, 0, 1) > 0) {
271  0 return this.oneByte[0];
272    }
273  0 return -1;
274    }
275   
 
276  132 toggle @Override
277    public int read(byte[] out, int offset, int length) throws IOException
278    {
279  132 if ((offset | length | (out.length - (length + offset)) | (offset + length)) < 0) {
280  0 throw new IndexOutOfBoundsException();
281    }
282   
283  132 if (length == 0) {
284  0 return 0;
285    }
286   
287  132 int readLen = 0;
288   
289  132 int off = offset;
290  132 int len = length;
291   
292    // Is some pending data of a previous call available ?
293  132 if (this.ofLen > 0) {
294  48 int clen = copyData(this.ofBuf, this.ofOff, this.ofLen, out, off, len);
295  48 this.ofLen -= clen;
296  48 this.ofOff += clen;
297  48 off += clen;
298  48 len -= clen;
299  48 readLen += clen;
300    }
301   
302    // Still some data to read ?
303  132 if (len > 0) {
304    // Try to read the needed block to get -len- bytes decoded
305  132 int blen = (len + this.blockSize - 1) / this.blockSize;
306  132 InputBuffer inBuf = new InputBuffer(blen);
307  132 int rlen = inBuf.getEffectiveLength();
308  132 if (rlen > 0) {
309  120 int rblen = (rlen + this.charSize - 1) / this.charSize;
310   
311    // Decode read data
312  120 ByteArrayOutputStream baos = new ByteArrayOutputStream(rblen * this.blockSize);
313  120 this.encoder.decode(inBuf.getBuffer(), 0, inBuf.getReadLength(), baos);
314  120 baos.close();
315   
316  120 int clen = copyData(baos.toByteArray(), 0, baos.size(), out, off, len);
317  120 readLen += clen;
318  120 this.ofLen = baos.size() - clen;
319  120 this.ofOff = 0;
320   
321  120 if (this.ofLen > 0) {
322  48 System.arraycopy(baos.toByteArray(), clen, this.ofBuf, this.ofOff, this.ofLen);
323    }
324    }
325    }
326   
327  132 return (readLen > 0) ? readLen : -1;
328    }
329   
 
330  0 toggle @Override
331    public int available() throws IOException
332    {
333  0 int len = super.available();
334  0 return ((len + this.charSize - 1) / this.charSize) * this.blockSize;
335    }
336   
 
337  0 toggle @Override
338    public boolean markSupported()
339    {
340  0 return false;
341    }
342   
 
343  168 toggle private int copyData(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff, int outLen)
344    {
345  168 int clen = inLen;
346  168 if (clen > outLen) {
347  48 clen = outLen;
348    }
349  168 System.arraycopy(inBuf, inOff, outBuf, outOff, clen);
350  168 return clen;
351    }
352    }