/* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.codec; import java.nio.ByteBuffer; import java.nio.CharBuffer; import org.cryptacular.EncodingException; /** * Base decoder class for encoding schemes described in RFC 3548. * * @author Middleware Services */ public abstract class AbstractBaseNDecoder implements Decoder { /** Block of encoded characters. */ private final char[] block = new char[getBlockLength() / getBitsPerChar()]; /** Decoding table. */ private final byte[] table; /** Current position in character block. */ private int blockPos; /** * Creates a new instance with given parameters. * * @param decodingTable Byte array indexed by characters in the character set encoding. */ public AbstractBaseNDecoder(final byte[] decodingTable) { table = decodingTable; } @Override public void decode(final CharBuffer input, final ByteBuffer output) throws EncodingException { char current; while (input.hasRemaining()) { current = input.get(); if (Character.isWhitespace(current)) { continue; } block[blockPos++] = current; if (blockPos == block.length) { writeOutput(output); } } } @Override public void finalize(final ByteBuffer output) throws EncodingException { if (blockPos > 0) { writeOutput(output); } } @Override public int outputSize(final int inputSize) { return inputSize * getBitsPerChar() / 8; } /** @return Number of bits in a block of encoded characters. */ protected abstract int getBlockLength(); /** @return Number of bits encoding a single character. */ protected abstract int getBitsPerChar(); /** * Writes bytes in the current encoding block to the output buffer. * * @param output Output buffer. */ private void writeOutput(final ByteBuffer output) { long b; long value = 0; int shift = getBlockLength(); for (char c : block) { if (c == '=') { break; } b = table[c & 0x7F]; if (b < 0) { throw new EncodingException("Invalid character " + c); } shift -= getBitsPerChar(); value |= b << shift; } final int stop = shift + getBitsPerChar() - 1; int offset = getBlockLength(); while (offset > stop) { offset -= 8; output.put((byte) ((value & (0xffL << offset)) >> offset)); } blockPos = 0; } }