View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * License.  You may obtain a copy of the License at:
    7    *
    8    *    http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  /*
    17   * Written by Robert Harder and released to the public domain, as explained at
    18   * http://creativecommons.org/licenses/publicdomain
    19   */
    20  package org.jboss.netty.handler.codec.base64;
    21
    22  import org.jboss.netty.buffer.ChannelBuffer;
    23  import org.jboss.netty.buffer.ChannelBufferFactory;
    24  import org.jboss.netty.buffer.HeapChannelBufferFactory;
    25
    26  /**
    27   * Utility class for {@link ChannelBuffer} that encodes and decodes to and from
    28   * <a href="http://en.wikipedia.org/wiki/Base64">Base64</a> notation.
    29   * <p>
    30   * The encoding and decoding algorithm in this class has been derived from
    31   * <a href="http://iharder.sourceforge.net/current/java/base64/">Robert Harder's Public Domain Base64 Encoder/Decoder</a>.
    32   *
    33   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    34   * @author Robert Harder (rob@iharder.net)
    35   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    36   * @version $Rev: 2241 $, $Date: 2010-04-16 13:12:43 +0900 (Fri, 16 Apr 2010) $
    37   *
    38   * @apiviz.landmark
    39   * @apiviz.uses org.jboss.netty.handler.codec.base64.Base64Dialect
    40   */
    41  public class Base64 {
    42
    43      /** Maximum line length (76) of Base64 output. */
    44      private static final int MAX_LINE_LENGTH = 76;
    45
    46      /** The equals sign (=) as a byte. */
    47      private static final byte EQUALS_SIGN = (byte) '=';
    48
    49      /** The new line character (\n) as a byte. */
    50      private static final byte NEW_LINE = (byte) '\n';
    51
    52      private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
    53
    54      private static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
    55
    56      private static final byte[] alphabet(Base64Dialect dialect) {
    57          if (dialect == null) {
    58              throw new NullPointerException("dialect");
    59          }
    60          return dialect.alphabet;
    61      }
    62
    63      private static final byte[] decodabet(Base64Dialect dialect) {
    64          if (dialect == null) {
    65              throw new NullPointerException("dialect");
    66          }
    67          return dialect.decodabet;
    68      }
    69
    70      private static final boolean breakLines(Base64Dialect dialect) {
    71          if (dialect == null) {
    72              throw new NullPointerException("dialect");
    73          }
    74          return dialect.breakLinesByDefault;
    75      }
    76
    77      public static ChannelBuffer encode(ChannelBuffer src) {
    78          return encode(src, Base64Dialect.STANDARD);
    79      }
    80
    81      public static ChannelBuffer encode(ChannelBuffer src, Base64Dialect dialect) {
    82          return encode(src, breakLines(dialect), dialect);
    83      }
    84
    85      public static ChannelBuffer encode(
    86              ChannelBuffer src, ChannelBufferFactory bufferFactory) {
    87          return encode(src, Base64Dialect.STANDARD, bufferFactory);
    88      }
    89
    90      public static ChannelBuffer encode(
    91              ChannelBuffer src, Base64Dialect dialect, ChannelBufferFactory bufferFactory) {
    92          return encode(src, breakLines(dialect), dialect, bufferFactory);
    93      }
    94
    95      public static ChannelBuffer encode(ChannelBuffer src, boolean breakLines) {
    96          return encode(src, breakLines, Base64Dialect.STANDARD);
    97      }
    98
    99      public static ChannelBuffer encode(
    100             ChannelBuffer src, boolean breakLines, Base64Dialect dialect) {
    101         return encode(src, breakLines, dialect, HeapChannelBufferFactory.getInstance());
    102     }
    103
    104     public static ChannelBuffer encode(
    105             ChannelBuffer src, boolean breakLines, ChannelBufferFactory bufferFactory) {
    106         return encode(src, breakLines, Base64Dialect.STANDARD, bufferFactory);
    107     }
    108
    109     public static ChannelBuffer encode(
    110             ChannelBuffer src, boolean breakLines, Base64Dialect dialect, ChannelBufferFactory bufferFactory) {
    111
    112         if (src == null) {
    113             throw new NullPointerException("src");
    114         }
    115
    116         ChannelBuffer dest = encode(
    117                 src, src.readerIndex(), src.readableBytes(), breakLines, dialect, bufferFactory);
    118         src.readerIndex(src.writerIndex());
    119         return dest;
    120     }
    121
    122     public static ChannelBuffer encode(ChannelBuffer src, int off, int len) {
    123         return encode(src, off, len, Base64Dialect.STANDARD);
    124     }
    125
    126     public static ChannelBuffer encode(ChannelBuffer src, int off, int len, Base64Dialect dialect) {
    127         return encode(src, off, len, breakLines(dialect), dialect);
    128     }
    129
    130     public static ChannelBuffer encode(ChannelBuffer src, int off, int len, ChannelBufferFactory bufferFactory) {
    131         return encode(src, off, len, Base64Dialect.STANDARD, bufferFactory);
    132     }
    133
    134     public static ChannelBuffer encode(ChannelBuffer src, int off, int len, Base64Dialect dialect, ChannelBufferFactory bufferFactory) {
    135         return encode(src, off, len, breakLines(dialect), dialect, bufferFactory);
    136     }
    137
    138     public static ChannelBuffer encode(
    139             ChannelBuffer src, int off, int len, boolean breakLines) {
    140         return encode(src, off, len, breakLines, Base64Dialect.STANDARD);
    141     }
    142
    143     public static ChannelBuffer encode(
    144             ChannelBuffer src, int off, int len,
    145             boolean breakLines, Base64Dialect dialect) {
    146         return encode(src, off, len, breakLines, dialect, HeapChannelBufferFactory.getInstance());
    147     }
    148
    149     public static ChannelBuffer encode(
    150             ChannelBuffer src, int off, int len,
    151             boolean breakLines, ChannelBufferFactory bufferFactory) {
    152         return encode(src, off, len, breakLines, Base64Dialect.STANDARD, bufferFactory);
    153     }
    154
    155     public static ChannelBuffer encode(
    156             ChannelBuffer src, int off, int len,
    157             boolean breakLines, Base64Dialect dialect, ChannelBufferFactory bufferFactory) {
    158
    159         if (src == null) {
    160             throw new NullPointerException("src");
    161         }
    162         if (dialect == null) {
    163             throw new NullPointerException("dialect");
    164         }
    165         if (bufferFactory == null) {
    166             throw new NullPointerException("bufferFactory");
    167         }
    168
    169         int len43 = len * 4 / 3;
    170         ChannelBuffer dest = bufferFactory.getBuffer(
    171                 src.order(),
    172                 len43 +
    173                 (len % 3 > 0? 4 : 0) + // Account for padding
    174                 (breakLines? len43 / MAX_LINE_LENGTH : 0)); // New lines
    175         int d = 0;
    176         int e = 0;
    177         int len2 = len - 2;
    178         int lineLength = 0;
    179         for (; d < len2; d += 3, e += 4) {
    180             encode3to4(src, d + off, 3, dest, e, dialect);
    181
    182             lineLength += 4;
    183             if (breakLines && lineLength == MAX_LINE_LENGTH) {
    184                 dest.setByte(e + 4, NEW_LINE);
    185                 e ++;
    186                 lineLength = 0;
    187             } // end if: end of line
    188         } // end for: each piece of array
    189
    190         if (d < len) {
    191             encode3to4(src, d + off, len - d, dest, e, dialect);
    192             e += 4;
    193         } // end if: some padding needed
    194
    195         return dest.slice(0, e);
    196     }
    197
    198     private static void encode3to4(
    199             ChannelBuffer src, int srcOffset, int numSigBytes,
    200             ChannelBuffer dest, int destOffset, Base64Dialect dialect) {
    201
    202         byte[] ALPHABET = alphabet(dialect);
    203
    204         //           1         2         3
    205         // 01234567890123456789012345678901 Bit position
    206         // --------000000001111111122222222 Array position from threeBytes
    207         // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
    208         //          >>18  >>12  >> 6  >> 0  Right shift necessary
    209         //                0x3f  0x3f  0x3f  Additional AND
    210
    211         // Create buffer with zero-padding if there are only one or two
    212         // significant bytes passed in the array.
    213         // We have to shift left 24 in order to flush out the 1's that appear
    214         // when Java treats a value as negative that is cast from a byte to an int.
    215         int inBuff =
    216                 (numSigBytes > 0? src.getByte(srcOffset    ) << 24 >>>  8 : 0) |
    217                 (numSigBytes > 1? src.getByte(srcOffset + 1) << 24 >>> 16 : 0) |
    218                 (numSigBytes > 2? src.getByte(srcOffset + 2) << 24 >>> 24 : 0);
    219
    220         switch (numSigBytes) {
    221         case 3:
    222             dest.setByte(destOffset    , ALPHABET[inBuff >>> 18       ]);
    223             dest.setByte(destOffset + 1, ALPHABET[inBuff >>> 12 & 0x3f]);
    224             dest.setByte(destOffset + 2, ALPHABET[inBuff >>>  6 & 0x3f]);
    225             dest.setByte(destOffset + 3, ALPHABET[inBuff        & 0x3f]);
    226             break;
    227         case 2:
    228             dest.setByte(destOffset    , ALPHABET[inBuff >>> 18       ]);
    229             dest.setByte(destOffset + 1, ALPHABET[inBuff >>> 12 & 0x3f]);
    230             dest.setByte(destOffset + 2, ALPHABET[inBuff >>> 6  & 0x3f]);
    231             dest.setByte(destOffset + 3, EQUALS_SIGN);
    232             break;
    233         case 1:
    234             dest.setByte(destOffset    , ALPHABET[inBuff >>> 18       ]);
    235             dest.setByte(destOffset + 1, ALPHABET[inBuff >>> 12 & 0x3f]);
    236             dest.setByte(destOffset + 2, EQUALS_SIGN);
    237             dest.setByte(destOffset + 3, EQUALS_SIGN);
    238             break;
    239         }
    240     }
    241
    242     public static ChannelBuffer decode(ChannelBuffer src) {
    243         return decode(src, Base64Dialect.STANDARD);
    244     }
    245
    246     public static ChannelBuffer decode(ChannelBuffer src, Base64Dialect dialect) {
    247         return decode(src, dialect, HeapChannelBufferFactory.getInstance());
    248     }
    249
    250     public static ChannelBuffer decode(ChannelBuffer src, ChannelBufferFactory bufferFactory) {
    251         return decode(src, Base64Dialect.STANDARD, bufferFactory);
    252     }
    253
    254     public static ChannelBuffer decode(ChannelBuffer src, Base64Dialect dialect, ChannelBufferFactory bufferFactory) {
    255
    256         if (src == null) {
    257             throw new NullPointerException("src");
    258         }
    259
    260         ChannelBuffer dest = decode(src, src.readerIndex(), src.readableBytes(), dialect, bufferFactory);
    261         src.readerIndex(src.writerIndex());
    262         return dest;
    263     }
    264
    265     public static ChannelBuffer decode(
    266             ChannelBuffer src, int off, int len) {
    267         return decode(src, off, len, Base64Dialect.STANDARD);
    268     }
    269
    270     public static ChannelBuffer decode(
    271             ChannelBuffer src, int off, int len, Base64Dialect dialect) {
    272         return decode(src, off, len, dialect, HeapChannelBufferFactory.getInstance());
    273     }
    274
    275     public static ChannelBuffer decode(
    276             ChannelBuffer src, int off, int len, ChannelBufferFactory bufferFactory) {
    277         return decode(src, off, len, Base64Dialect.STANDARD, bufferFactory);
    278     }
    279
    280     public static ChannelBuffer decode(
    281             ChannelBuffer src, int off, int len, Base64Dialect dialect,
    282             ChannelBufferFactory bufferFactory) {
    283
    284         if (src == null) {
    285             throw new NullPointerException("src");
    286         }
    287         if (dialect == null) {
    288             throw new NullPointerException("dialect");
    289         }
    290         if (bufferFactory == null) {
    291             throw new NullPointerException("bufferFactory");
    292         }
    293
    294         byte[] DECODABET = decodabet(dialect);
    295
    296         int len34 = len * 3 / 4;
    297         ChannelBuffer dest = bufferFactory.getBuffer(src.order(), len34); // Upper limit on size of output
    298         int outBuffPosn = 0;
    299
    300         byte[] b4 = new byte[4];
    301         int b4Posn = 0;
    302         int i = 0;
    303         byte sbiCrop = 0;
    304         byte sbiDecode = 0;
    305         for (i = off; i < off + len; i ++) {
    306             sbiCrop = (byte) (src.getByte(i) & 0x7f); // Only the low seven bits
    307             sbiDecode = DECODABET[sbiCrop];
    308
    309             if (sbiDecode >= WHITE_SPACE_ENC) { // White space, Equals sign or better
    310                 if (sbiDecode >= EQUALS_SIGN_ENC) {
    311                     b4[b4Posn ++] = sbiCrop;
    312                     if (b4Posn > 3) {
    313                         outBuffPosn += decode4to3(
    314                                 b4, 0, dest, outBuffPosn, dialect);
    315                         b4Posn = 0;
    316
    317                         // If that was the equals sign, break out of 'for' loop
    318                         if (sbiCrop == EQUALS_SIGN) {
    319                             break;
    320                         }
    321                     } // end if: quartet built
    322                 } // end if: equals sign or better
    323             } // end if: white space, equals sign or better
    324             else {
    325                 throw new IllegalArgumentException(
    326                         "bad Base64 input character at " + i + ": " +
    327                         src.getUnsignedByte(i) + " (decimal)");
    328             }
    329         }
    330
    331         return dest.slice(0, outBuffPosn);
    332     }
    333
    334     private static int decode4to3(
    335             byte[] src, int srcOffset,
    336             ChannelBuffer dest, int destOffset, Base64Dialect dialect) {
    337
    338         byte[] DECODABET = decodabet(dialect);
    339
    340         // Example: Dk==
    341         if (src[srcOffset + 2] == EQUALS_SIGN) {
    342             int outBuff =
    343                     (DECODABET[src[srcOffset    ]] & 0xFF) << 18 |
    344                     (DECODABET[src[srcOffset + 1]] & 0xFF) << 12;
    345
    346             dest.setByte(destOffset, (byte) (outBuff >>> 16));
    347             return 1;
    348         }
    349
    350         // Example: DkL=
    351         else if (src[srcOffset + 3] == EQUALS_SIGN) {
    352             int outBuff =
    353                     (DECODABET[src[srcOffset    ]] & 0xFF) << 18 |
    354                     (DECODABET[src[srcOffset + 1]] & 0xFF) << 12 |
    355                     (DECODABET[src[srcOffset + 2]] & 0xFF) <<  6;
    356
    357             dest.setByte(destOffset    , (byte) (outBuff >>> 16));
    358             dest.setByte(destOffset + 1, (byte) (outBuff >>>  8));
    359             return 2;
    360         }
    361
    362         // Example: DkLE
    363         else {
    364             int outBuff;
    365             try {
    366                 outBuff =
    367                         (DECODABET[src[srcOffset    ]] & 0xFF) << 18 |
    368                         (DECODABET[src[srcOffset + 1]] & 0xFF) << 12 |
    369                         (DECODABET[src[srcOffset + 2]] & 0xFF) <<  6 |
    370                          DECODABET[src[srcOffset + 3]] & 0xFF;
    371             } catch (IndexOutOfBoundsException e) {
    372                 throw new IllegalArgumentException("not encoded in Base64");
    373             }
    374
    375             dest.setByte(destOffset    , (byte) (outBuff >> 16));
    376             dest.setByte(destOffset + 1, (byte) (outBuff >>  8));
    377             dest.setByte(destOffset + 2, (byte)  outBuff);
    378             return 3;
    379         }
    380     }
    381
    382     private Base64() {
    383         // Unused
    384     }
    385 }