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<Byte, Integer> lookup64 = new HashMap<Byte, Integer>();
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    @Override
069    public int read(byte[] b, int off, int len) throws IOException
070    {
071        for (int i = 0; i < len; ++i)
072        {
073            int val = read();
074            if (val == -1)
075            {
076                return i == 0 ? -1 : i;
077            }
078            b[off + i] = (byte)val;
079        }
080        return len;
081    }
082
083
084    @Override
085    public int read() throws IOException
086    {
087        if (charsInBuf == 0)
088        {
089            fillBuffer();
090            if (charsInBuf == 0)
091            {
092                return -1;
093            }
094        }
095        
096        return (buf >> (--charsInBuf * 8)) & 0xff;
097    }
098    
099    private void fillBuffer() throws IOException
100    {
101        //Read next 4 characters
102        int bitsRead = 0;
103        while (bitsRead < 24)
104        {
105            int val = in.read();
106            if (val == -1 || val == '=') break;
107
108            Integer lval = (Integer)lookup64.get(new Byte((byte)val));
109            if (lval == null) continue;
110
111            buf = buf << 6 | lval.byteValue();
112            bitsRead += 6;
113        }
114
115        switch (bitsRead)
116        {
117            case 6:
118            {
119                throw new RuntimeException("Invalid termination of base64 encoding.");
120            }
121            case 12:
122            {
123                buf >>= 4;
124                bitsRead = 8;
125                break;
126            }
127            case 18:
128            {
129                buf >>= 2;
130                bitsRead = 16;
131                break;
132            }
133            case 0:
134            case 24:
135            {
136                break;
137            }
138            default:
139            {
140                assert false : "Should never encounter other bit counts";
141            }
142        }
143
144        charsInBuf = bitsRead / 8;
145    }
146}