001/*
002 * SVG Salamander
003 * Copyright (c) 2004, Mark McKay
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or 
007 * without modification, are permitted provided that the following
008 * conditions are met:
009 *
010 *   - Redistributions of source code must retain the above 
011 *     copyright notice, this list of conditions and the following
012 *     disclaimer.
013 *   - Redistributions in binary form must reproduce the above
014 *     copyright notice, this list of conditions and the following
015 *     disclaimer in the documentation and/or other materials 
016 *     provided with the distribution.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
029 * OF THE POSSIBILITY OF SUCH DAMAGE. 
030 * 
031 * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
032 * projects can be found at http://www.kitfox.com
033 *
034 * Created on July 23, 2007
035 */
036
037package com.kitfox.svg.util;
038
039import java.io.FilterInputStream;
040import java.io.IOException;
041import java.io.InputStream;
042import java.util.HashMap;
043
044/**
045 *
046 * @author kitfox
047 */
048public class Base64InputStream extends FilterInputStream implements Base64Consts
049{
050    static final HashMap lookup64 = new HashMap();
051    static {
052        byte[] ch = BASE64_CHARS.getBytes();
053        for (int i = 0; i < ch.length; i++)
054        {
055            lookup64.put(new Byte(ch[i]), new Integer(i));
056        }
057    }
058    
059    int buf;
060    int charsInBuf;
061    
062    /** Creates a new instance of Base64InputStream */
063    public Base64InputStream(InputStream in)
064    {
065        super(in);
066    }
067
068    public int read(byte[] b, int off, int len) throws IOException
069    {
070        for (int i = 0; i < len; ++i)
071        {
072            int val = read();
073            if (val == -1)
074            {
075                return i == 0 ? -1 : i;
076            }
077            b[off + i] = (byte)val;
078        }
079        return len;
080    }
081
082
083    public int read() throws IOException
084    {
085        if (charsInBuf == 0)
086        {
087            fillBuffer();
088            if (charsInBuf == 0)
089            {
090                return -1;
091            }
092        }
093        
094        return (buf >> (--charsInBuf * 8)) & 0xff;
095    }
096    
097    private void fillBuffer() throws IOException
098    {
099        //Read next 4 characters
100        int bitsRead = 0;
101        while (bitsRead < 24)
102        {
103            int val = in.read();
104            if (val == -1 || val == '=') break;
105
106            Integer lval = (Integer)lookup64.get(new Byte((byte)val));
107            if (lval == null) continue;
108
109            buf = buf << 6 | lval.byteValue();
110            bitsRead += 6;
111        }
112
113        switch (bitsRead)
114        {
115            case 6:
116            {
117                throw new RuntimeException("Invalid termination of base64 encoding.");
118            }
119            case 12:
120            {
121                buf >>= 4;
122                bitsRead = 8;
123                break;
124            }
125            case 18:
126            {
127                buf >>= 2;
128                bitsRead = 16;
129                break;
130            }
131            case 0:
132            case 24:
133            {
134                break;
135            }
136            default:
137            {
138                assert false : "Should never encounter other bit counts";
139            }
140        }
141
142        charsInBuf = bitsRead / 8;
143    }
144}