Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2012 The Guava Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
   * in compliance with the License. You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed under the License
  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
 
 package com.google.common.io;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkPositionIndexes;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.io.GwtWorkarounds.asCharInput;
 import static com.google.common.io.GwtWorkarounds.stringBuilderOutput;
 import static com.google.common.math.IntMath.divide;
 import static com.google.common.math.IntMath.log2;
 import static java.math.RoundingMode.CEILING;
 import static java.math.RoundingMode.FLOOR;
 import static java.math.RoundingMode.UNNECESSARY;
 
 
 
A binary encoding scheme for reversibly translating between byte sequences and printable ASCII strings. This class includes several constants for encoding schemes specified by RFC 4648. For example, the expression:
   BaseEncoding.base32().encode("foo".getBytes(Charsets.US_ASCII))

returns the string "MZXW6===", and

   byte[] decoded = BaseEncoding.base32().decode("MZXW6===");

...returns the ASCII bytes of the string "foo".

By default, BaseEncoding's behavior is relatively strict and in accordance with RFC 4648. Decoding rejects characters in the wrong case, though padding is optional. To modify encoding and decoding behavior, use configuration methods to obtain a new encoding with modified behavior:

   BaseEncoding.base16().lowerCase().decode("deadbeef");

Warning: BaseEncoding instances are immutable. Invoking a configuration method has no effect on the receiving instance; you must store and use the new encoding instance it returns, instead.

   // Do NOT do this
   BaseEncoding hex = BaseEncoding.base16();
   hex.lowerCase(); // does nothing!
   return hex.decode("deadbeef"); // throws an IllegalArgumentException

It is guaranteed that encoding.decode(encoding.encode(x)) is always equal to x, but the reverse does not necessarily hold.

Encoding Alphabet char:byte ratio Default padding Comments
base16() 0-9 A-F 2.00 N/A Traditional hexadecimal. Defaults to upper case.
base32() A-Z 2-7 1.60 = Human-readable; no possibility of mixing up 0/O or 1/I. Defaults to upper case.
base32Hex() 0-9 A-V 1.60 = "Numerical" base 32; extended from the traditional hex alphabet. Defaults to upper case.
base64() A-Z a-z 0-9 + / 1.33 =
base64Url() A-Z a-z 0-9 - _ 1.33 = Safe to use as filenames, or to pass in URLs without escaping

All instances of this class are immutable, so they may be stored safely as static constants.

Author(s):
Louis Wasserman
Since:
14.0
@GwtCompatible(emulated = true)
public abstract class BaseEncoding {
  // TODO(user): consider adding encodeTo(Appendable, byte[], [int, int])
  BaseEncoding() {}

  
Exception indicating invalid base-encoded input encountered while decoding.

Author(s):
Louis Wasserman
Since:
15.0
  public static final class DecodingException extends IOException {
    DecodingException(String message) {
      super(message);
    }
    DecodingException(Throwable cause) {
      super(cause);
    }
  }

  
Encodes the specified byte array, and returns the encoded String.
  public String encode(byte[] bytes) {
    return encode(checkNotNull(bytes), 0, bytes.length);
  }

  
Encodes the specified range of the specified byte array, and returns the encoded String.
  public final String encode(byte[] bytesint offint len) {
    checkNotNull(bytes);
    checkPositionIndexes(offoff + lenbytes.length);
    ByteOutput byteOutput = encodingStream(result);
    try {
      for (int i = 0; i < leni++) {
        byteOutput.write(bytes[off + i]);
      }
      byteOutput.close();
    } catch (IOException impossible) {
      throw new AssertionError("impossible");
    }
    return result.toString();
  }
  // TODO(user): document the extent of leniency, probably after adding ignore(CharMatcher)
  private static byte[] extract(byte[] resultint length) {
    if (length == result.length) {
      return result;
    } else {
      byte[] trunc = new byte[length];
      System.arraycopy(result, 0, trunc, 0, length);
      return trunc;
    }
  }

  
Decodes the specified character sequence, and returns the resulting byte[]. This is the inverse operation to encode(byte[]).

Throws:
java.lang.IllegalArgumentException if the input is not a valid encoded string according to this encoding.
  public final byte[] decode(CharSequence chars) {
    try {
      return decodeChecked(chars);
    } catch (DecodingException badInput) {
      throw new IllegalArgumentException(badInput);
    }
  }

  
Decodes the specified character sequence, and returns the resulting byte[]. This is the inverse operation to encode(byte[]).

Throws:
BaseEncoding.DecodingException if the input is not a valid encoded string according to this encoding.
  final byte[] decodeChecked(CharSequence charsthrows DecodingException {
    chars = padding().trimTrailingFrom(chars);
    ByteInput decodedInput = decodingStream(asCharInput(chars));
    byte[] tmp = new byte[maxDecodedSize(chars.length())];
    int index = 0;
    try {
      for (int i = decodedInput.read(); i != -1; i = decodedInput.read()) {
        tmp[index++] = (bytei;
      }
    } catch (DecodingException badInput) {
      throw badInput;
    } catch (IOException impossible) {
      throw new AssertionError(impossible);
    }
    return extract(tmpindex);
  }
  // Implementations for encoding/decoding
  abstract int maxEncodedSize(int bytes);
  abstract ByteOutput encodingStream(CharOutput charOutput);
  abstract int maxDecodedSize(int chars);
  abstract ByteInput decodingStream(CharInput charInput);
  abstract CharMatcher padding();
  // Modified encoding generators

  
Returns an encoding that behaves equivalently to this encoding, but omits any padding characters as specified by RFC 4648 section 3.2, Padding of Encoded Data.
  public abstract BaseEncoding omitPadding();

  
Returns an encoding that behaves equivalently to this encoding, but uses an alternate character for padding.

Throws:
java.lang.IllegalArgumentException if this padding character is already used in the alphabet or a separator
  public abstract BaseEncoding withPadChar(char padChar);

  
Returns an encoding that behaves equivalently to this encoding, but adds a separator string after every n characters. Any occurrences of any characters that occur in the separator are skipped over in decoding.

Throws:
java.lang.IllegalArgumentException if any alphabet or padding characters appear in the separator string, or if n <= 0
java.lang.UnsupportedOperationException if this encoding already uses a separator
  public abstract BaseEncoding withSeparator(String separatorint n);

  
Returns an encoding that behaves equivalently to this encoding, but encodes and decodes with uppercase letters. Padding and separator characters remain in their original case.

Throws:
java.lang.IllegalStateException if the alphabet used by this encoding contains mixed upper- and lower-case characters
  public abstract BaseEncoding upperCase();

  
Returns an encoding that behaves equivalently to this encoding, but encodes and decodes with lowercase letters. Padding and separator characters remain in their original case.

Throws:
java.lang.IllegalStateException if the alphabet used by this encoding contains mixed upper- and lower-case characters
  public abstract BaseEncoding lowerCase();
  private static final BaseEncoding BASE64 = new StandardBaseEncoding(
      "base64()""ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"'=');

  
The "base64" base encoding specified by RFC 4648 section 4, Base 64 Encoding. (This is the same as the base 64 encoding from RFC 3548.)

The character '=' is used for padding, but can be omitted or replaced.

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in Encoded Data. Line feeds may be added using withSeparator(java.lang.String,int).

  public static BaseEncoding base64() {
    return ;
  }
  private static final BaseEncoding BASE64_URL = new StandardBaseEncoding(
      "base64Url()""ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"'=');

  
The "base64url" encoding specified by RFC 4648 section 5, Base 64 Encoding with URL and Filename Safe Alphabet, also sometimes referred to as the "web safe Base64." (This is the same as the base 64 encoding with URL and filename safe alphabet from RFC 3548.)

The character '=' is used for padding, but can be omitted or replaced.

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in Encoded Data. Line feeds may be added using withSeparator(java.lang.String,int).

  public static BaseEncoding base64Url() {
    return ;
  }
  private static final BaseEncoding BASE32 =
      new StandardBaseEncoding("base32()""ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"'=');

  
The "base32" encoding specified by RFC 4648 section 6, Base 32 Encoding. (This is the same as the base 32 encoding from RFC 3548.)

The character '=' is used for padding, but can be omitted or replaced.

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in Encoded Data. Line feeds may be added using withSeparator(java.lang.String,int).

  public static BaseEncoding base32() {
    return ;
  }
  private static final BaseEncoding BASE32_HEX =
      new StandardBaseEncoding("base32Hex()""0123456789ABCDEFGHIJKLMNOPQRSTUV"'=');

  
The "base32hex" encoding specified by RFC 4648 section 7, Base 32 Encoding with Extended Hex Alphabet. There is no corresponding encoding in RFC 3548.

The character '=' is used for padding, but can be omitted or replaced.

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in Encoded Data. Line feeds may be added using withSeparator(java.lang.String,int).

  public static BaseEncoding base32Hex() {
    return ;
  }
  private static final BaseEncoding BASE16 =
      new StandardBaseEncoding("base16()""0123456789ABCDEF"null);

  
The "base16" encoding specified by RFC 4648 section 8, Base 16 Encoding. (This is the same as the base 16 encoding from RFC 3548.) This is commonly known as "hexadecimal" format.

No padding is necessary in base 16, so withPadChar(char) and omitPadding() have no effect.

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in Encoded Data. Line feeds may be added using withSeparator(java.lang.String,int).

  public static BaseEncoding base16() {
    return ;
  }
  private static final class Alphabet extends CharMatcher {
    private final String name;
    // this is meant to be immutable -- don't modify it!
    private final char[] chars;
    final int mask;
    final int bitsPerChar;
    final int charsPerChunk;
    final int bytesPerChunk;
    private final byte[] decodabet;
    private final boolean[] validPadding;
    Alphabet(String namechar[] chars) {
      this. = checkNotNull(name);
      this. = checkNotNull(chars);
      try {
        this. = log2(chars.length);
      } catch (ArithmeticException e) {
        throw new IllegalArgumentException("Illegal alphabet length " + chars.lengthe);
      }
      /*
       * e.g. for base64, bitsPerChar == 6, charsPerChunk == 4, and bytesPerChunk == 3. This makes
       * for the smallest chunk size that still has charsPerChunk * bitsPerChar be a multiple of 8.
       */
      int gcd = Math.min(8, Integer.lowestOneBit());
      this. = 8 / gcd;
      this. =  / gcd;
      this. = chars.length - 1;
      byte[] decodabet = new byte[. + 1];
      Arrays.fill(decodabet, (byte) -1);
      for (int i = 0; i < chars.lengthi++) {
        char c = chars[i];
        checkArgument(..matches(c), "Non-ASCII character: %s"c);
        checkArgument(decodabet[c] == -1, "Duplicate character: %s"c);
        decodabet[c] = (bytei;
      }
      this. = decodabet;
      boolean[] validPadding = new boolean[];
      for (int i = 0; i < i++) {
        validPadding[divide(i * 8, )] = true;
      }
      this. = validPadding;
    }
    char encode(int bits) {
      return [bits];
    }
    boolean isValidPaddingStartPosition(int index) {
      return [index % ];
    }
    int decode(char chthrows IOException {
      if (ch > . || [ch] == -1) {
        throw new DecodingException("Unrecognized character: " + ch);
      }
      return [ch];
    }
    private boolean hasLowerCase() {
      for (char c : ) {
        if (Ascii.isLowerCase(c)) {
          return true;
        }
      }
      return false;
    }
    private boolean hasUpperCase() {
      for (char c : ) {
        if (Ascii.isUpperCase(c)) {
          return true;
        }
      }
      return false;
    }
    Alphabet upperCase() {
      if (!hasLowerCase()) {
        return this;
      } else {
        checkState(!hasUpperCase(), "Cannot call upperCase() on a mixed-case alphabet");
        char[] upperCased = new char[.];
        for (int i = 0; i < .i++) {
          upperCased[i] = Ascii.toUpperCase([i]);
        }
        return new Alphabet( + ".upperCase()"upperCased);
      }
    }
    Alphabet lowerCase() {
      if (!hasUpperCase()) {
        return this;
      } else {
        checkState(!hasLowerCase(), "Cannot call lowerCase() on a mixed-case alphabet");
        char[] lowerCased = new char[.];
        for (int i = 0; i < .i++) {
          lowerCased[i] = Ascii.toLowerCase([i]);
        }
        return new Alphabet( + ".lowerCase()"lowerCased);
      }
    }
    @Override
    public boolean matches(char c) {
      return ..matches(c) && [c] != -1;
    }
    @Override
    public String toString() {
      return ;
    }
  }
  static final class StandardBaseEncoding extends BaseEncoding {
    // TODO(user): provide a useful toString
    private final Alphabet alphabet;
    @Nullable
    private final Character paddingChar;
    StandardBaseEncoding(String nameString alphabetChars, @Nullable Character paddingChar) {
      this(new Alphabet(namealphabetChars.toCharArray()), paddingChar);
    }
    StandardBaseEncoding(Alphabet alphabet, @Nullable Character paddingChar) {
      this. = checkNotNull(alphabet);
      checkArgument(paddingChar == null || !alphabet.matches(paddingChar),
          "Padding character %s was already in alphabet"paddingChar);
      this. = paddingChar;
    }
    @Override
    CharMatcher padding() {
      return ( == null) ? . : CharMatcher.is(.charValue());
    }
    @Override
    int maxEncodedSize(int bytes) {
      return . * divide(bytes.);
    }
    @Override
    ByteOutput encodingStream(final CharOutput out) {
      checkNotNull(out);
      return new ByteOutput() {
        int bitBuffer = 0;
        int bitBufferLength = 0;
        int writtenChars = 0;
        @Override
        public void write(byte bthrows IOException {
           <<= 8;
           |= b & 0xFF;
           += 8;
          while ( >= .) {
            int charIndex = ( >> ( - .))
                & .;
            out.write(.encode(charIndex));
            ++;
             -= .;
          }
        }
        @Override
        public void flush() throws IOException {
          out.flush();
        }
        @Override
        public void close() throws IOException {
          if ( > 0) {
            int charIndex = ( << (. - ))
                & .;
            out.write(.encode(charIndex));
            ++;
            if ( != null) {
              while ( % . != 0) {
                out.write(.charValue());
                ++;
              }
            }
          }
          out.close();
        }
      };
    }
    @Override
    int maxDecodedSize(int chars) {
      return (int) ((. * (longchars + 7L) / 8L);
    }
    @Override
    ByteInput decodingStream(final CharInput reader) {
      checkNotNull(reader);
      return new ByteInput() {
        int bitBuffer = 0;
        int bitBufferLength = 0;
        int readChars = 0;
        boolean hitPadding = false;
        final CharMatcher paddingMatcher = padding();
        @Override
        public int read() throws IOException {
          while (true) {
            int readChar = reader.read();
            if (readChar == -1) {
              if (! && !.isValidPaddingStartPosition()) {
                throw new DecodingException("Invalid input length " + );
              }
              return -1;
            }
            ++;
            char ch = (charreadChar;
            if (.matches(ch)) {
              if (!
                  && ( == 1 || !.isValidPaddingStartPosition( - 1))) {
                throw new DecodingException("Padding cannot start at index " + );
              }
               = true;
            } else if () {
              throw new DecodingException(
                  "Expected padding character but found '" + ch + "' at index " + );
            } else {
               <<= .;
               |= .decode(ch);
               += .;
              if ( >= 8) {
                 -= 8;
                return ( >> ) & 0xFF;
              }
            }
          }
        }
        @Override
        public void close() throws IOException {
          reader.close();
        }
      };
    }
    @Override
    public BaseEncoding omitPadding() {
      return ( == null) ? this : new StandardBaseEncoding(null);
    }
    @Override
    public BaseEncoding withPadChar(char padChar) {
      if (8 % . == 0 ||
          ( != null && .charValue() == padChar)) {
        return this;
      } else {
        return new StandardBaseEncoding(padChar);
      }
    }
    @Override
    public BaseEncoding withSeparator(String separatorint afterEveryChars) {
      checkNotNull(separator);
      checkArgument(padding().or().matchesNoneOf(separator),
          "Separator cannot contain alphabet or padding characters");
      return new SeparatedBaseEncoding(thisseparatorafterEveryChars);
    }
    private transient BaseEncoding upperCase;
    private transient BaseEncoding lowerCase;
    @Override
    public BaseEncoding upperCase() {
      BaseEncoding result = ;
      if (result == null) {
        Alphabet upper = .upperCase();
        result =  =
            (upper == ) ? this : new StandardBaseEncoding(upper);
      }
      return result;
    }
    @Override
    public BaseEncoding lowerCase() {
      BaseEncoding result = ;
      if (result == null) {
        Alphabet lower = .lowerCase();
        result =  =
            (lower == ) ? this : new StandardBaseEncoding(lower);
      }
      return result;
    }
    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder("BaseEncoding.");
      builder.append(.toString());
      if (8 % . != 0) {
        if ( == null) {
          builder.append(".omitPadding()");
        } else {
          builder.append(".withPadChar(").append().append(')');
        }
      }
      return builder.toString();
    }
  }
  static CharInput ignoringInput(final CharInput delegatefinal CharMatcher toIgnore) {
    checkNotNull(delegate);
    checkNotNull(toIgnore);
    return new CharInput() {
      @Override
      public int read() throws IOException {
        int readChar;
        do {
          readChar = delegate.read();
        } while (readChar != -1 && toIgnore.matches((charreadChar));
        return readChar;
      }
      @Override
      public void close() throws IOException {
        delegate.close();
      }
    };
  }
      final CharOutput delegatefinal String separatorfinal int afterEveryChars) {
    checkNotNull(delegate);
    checkNotNull(separator);
    checkArgument(afterEveryChars > 0);
    return new CharOutput() {
      int charsUntilSeparator = afterEveryChars;
      @Override
      public void write(char cthrows IOException {
        if ( == 0) {
          for (int i = 0; i < separator.length(); i++) {
            delegate.write(separator.charAt(i));
          }
           = afterEveryChars;
        }
        delegate.write(c);
        --;
      }
      @Override
      public void flush() throws IOException {
        delegate.flush();
      }
      @Override
      public void close() throws IOException {
        delegate.close();
      }
    };
  }
  static final class SeparatedBaseEncoding extends BaseEncoding {
    private final BaseEncoding delegate;
    private final String separator;
    private final int afterEveryChars;
    private final CharMatcher separatorChars;
    SeparatedBaseEncoding(BaseEncoding delegateString separatorint afterEveryChars) {
      this. = checkNotNull(delegate);
      this. = checkNotNull(separator);
      this. = afterEveryChars;
      checkArgument(
          afterEveryChars > 0, "Cannot add a separator after every %s chars"afterEveryChars);
      this. = CharMatcher.anyOf(separator).precomputed();
    }
    @Override
    CharMatcher padding() {
      return .padding();
    }
    @Override
    int maxEncodedSize(int bytes) {
      int unseparatedSize = .maxEncodedSize(bytes);
      return unseparatedSize + .length()
          * divide(Math.max(0, unseparatedSize - 1), );
    }
    @Override
    ByteOutput encodingStream(final CharOutput output) {
    }
    @Override
    int maxDecodedSize(int chars) {
      return .maxDecodedSize(chars);
    }
    @Override
    ByteInput decodingStream(final CharInput input) {
    }
    @Override
    public BaseEncoding omitPadding() {
    }
    @Override
    public BaseEncoding withPadChar(char padChar) {
    }
    @Override
    public BaseEncoding withSeparator(String separatorint afterEveryChars) {
      throw new UnsupportedOperationException("Already have a separator");
    }
    @Override
    public BaseEncoding upperCase() {
    }
    @Override
    public BaseEncoding lowerCase() {
    }
    @Override
    public String toString() {
      return .toString() +
          ".withSeparator(\"" +  + "\", " +  + ")";
    }
  }
New to GrepCode? Check out our FAQ X