Base16Encoding(String name, String alphabetChars) { this(new Alphabet(name, alphabetChars.toCharArray())); }
@Override public boolean canDecode(CharSequence chars) { chars = padding().trimTrailingFrom(chars); if (!alphabet.isValidPaddingStartPosition(chars.length())) { return false; } for (int i = 0; i < chars.length(); i++) { if (!alphabet.canDecode(chars.charAt(i))) { return false; } } return true; }
@Override int decodeTo(byte[] target, CharSequence chars) throws DecodingException { checkNotNull(target); chars = padding().trimTrailingFrom(chars); if (!alphabet.isValidPaddingStartPosition(chars.length())) { throw new DecodingException("Invalid input length " + chars.length()); } int bytesWritten = 0; for (int charIdx = 0; charIdx < chars.length(); charIdx += alphabet.charsPerChunk) { long chunk = 0; int charsProcessed = 0; for (int i = 0; i < alphabet.charsPerChunk; i++) { chunk <<= alphabet.bitsPerChar; if (charIdx + i < chars.length()) { chunk |= alphabet.decode(chars.charAt(charIdx + charsProcessed++)); } } final int minOffset = alphabet.bytesPerChunk * 8 - charsProcessed * alphabet.bitsPerChar; for (int offset = (alphabet.bytesPerChunk - 1) * 8; offset >= minOffset; offset -= 8) { target[bytesWritten++] = (byte) ((chunk >>> offset) & 0xFF); } } return bytesWritten; }
int readChar = reader.read(); if (readChar == -1) { if (!hitPadding && !alphabet.isValidPaddingStartPosition(readChars)) { throw new DecodingException("Invalid input length " + readChars); if (paddingMatcher.matches(ch)) { if (!hitPadding && (readChars == 1 || !alphabet.isValidPaddingStartPosition(readChars - 1))) { throw new DecodingException("Padding cannot start at index " + readChars); } else { bitBuffer <<= alphabet.bitsPerChar; bitBuffer |= alphabet.decode(ch); bitBufferLength += alphabet.bitsPerChar;
void encodeChunkTo(Appendable target, byte[] bytes, int off, int len) throws IOException { checkNotNull(target); checkPositionIndexes(off, off + len, bytes.length); checkArgument(len <= alphabet.bytesPerChunk); long bitBuffer = 0; for (int i = 0; i < len; ++i) { bitBuffer |= bytes[off + i] & 0xFF; bitBuffer <<= 8; // Add additional zero byte in the end. } // Position of first character is length of bitBuffer minus bitsPerChar. final int bitOffset = (len + 1) * 8 - alphabet.bitsPerChar; int bitsProcessed = 0; while (bitsProcessed < len * 8) { int charIndex = (int) (bitBuffer >>> (bitOffset - bitsProcessed)) & alphabet.mask; target.append(alphabet.encode(charIndex)); bitsProcessed += alphabet.bitsPerChar; } if (paddingChar != null) { while (bitsProcessed < alphabet.bytesPerChunk * 8) { target.append(paddingChar.charValue()); bitsProcessed += alphabet.bitsPerChar; } } }
@Override int decodeTo(byte[] target, CharSequence chars) throws DecodingException { checkNotNull(target); chars = padding().trimTrailingFrom(chars); if (!alphabet.isValidPaddingStartPosition(chars.length())) { throw new DecodingException("Invalid input length " + chars.length()); } int bytesWritten = 0; for (int i = 0; i < chars.length(); ) { int chunk = alphabet.decode(chars.charAt(i++)) << 18; chunk |= alphabet.decode(chars.charAt(i++)) << 12; target[bytesWritten++] = (byte) (chunk >>> 16); if (i < chars.length()) { chunk |= alphabet.decode(chars.charAt(i++)) << 6; target[bytesWritten++] = (byte) ((chunk >>> 8) & 0xFF); if (i < chars.length()) { chunk |= alphabet.decode(chars.charAt(i++)); target[bytesWritten++] = (byte) (chunk & 0xFF); } } } return bytesWritten; }
@Override void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOException { checkNotNull(target); checkPositionIndexes(off, off + len, bytes.length); int i = off; for (int remaining = len; remaining >= 3; remaining -= 3) { int chunk = (bytes[i++] & 0xFF) << 16 | (bytes[i++] & 0xFF) << 8 | bytes[i++] & 0xFF; target.append(alphabet.encode(chunk >>> 18)); target.append(alphabet.encode((chunk >>> 12) & 0x3F)); target.append(alphabet.encode((chunk >>> 6) & 0x3F)); target.append(alphabet.encode(chunk & 0x3F)); } if (i < off + len) { encodeChunkTo(target, bytes, i, off + len - i); } }
@Override public void close() throws IOException { if (bitBufferLength > 0) { int charIndex = (bitBuffer << (alphabet.bitsPerChar - bitBufferLength)) & alphabet.mask; out.write(alphabet.encode(charIndex)); writtenChars++; if (paddingChar != null) { while (writtenChars % alphabet.charsPerChunk != 0) { out.write(paddingChar.charValue()); writtenChars++; } } } out.close(); } };
Base64Encoding(String name, String alphabetChars, @Nullable Character paddingChar) { this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar); }
StandardBaseEncoding(String name, String alphabetChars, @Nullable Character paddingChar) { this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar); }
@Override int decodeTo(byte[] target, CharSequence chars) throws DecodingException { checkNotNull(target); if (chars.length() % 2 == 1) { throw new DecodingException("Invalid input length " + chars.length()); } int bytesWritten = 0; for (int i = 0; i < chars.length(); i += 2) { int decoded = alphabet.decode(chars.charAt(i)) << 4 | alphabet.decode(chars.charAt(i + 1)); target[bytesWritten++] = (byte) decoded; } return bytesWritten; }
@Override public BaseEncoding lowerCase() { BaseEncoding result = lowerCase; if (result == null) { Alphabet lower = alphabet.lowerCase(); result = lowerCase = (lower == alphabet) ? this : newInstance(lower, paddingChar); } return result; }
@Override public void write(int b) throws IOException { bitBuffer <<= 8; bitBuffer |= b & 0xFF; bitBufferLength += 8; while (bitBufferLength >= alphabet.bitsPerChar) { int charIndex = (bitBuffer >> (bitBufferLength - alphabet.bitsPerChar)) & alphabet.mask; out.write(alphabet.encode(charIndex)); writtenChars++; bitBufferLength -= alphabet.bitsPerChar; } }
@Override public int hashCode() { return alphabet.hashCode() ^ Objects.hashCode(paddingChar); } }
@Override public boolean equals(@Nullable Object other) { if (other instanceof StandardBaseEncoding) { StandardBaseEncoding that = (StandardBaseEncoding) other; return this.alphabet.equals(that.alphabet) && Objects.equal(this.paddingChar, that.paddingChar); } return false; }
Alphabet upperCase() { if (!hasLowerCase()) { return this; } else { checkState(!hasUpperCase(), "Cannot call upperCase() on a mixed-case alphabet"); char[] upperCased = new char[chars.length]; for (int i = 0; i < chars.length; i++) { upperCased[i] = Ascii.toUpperCase(chars[i]); } return new Alphabet(name + ".upperCase()", upperCased); } }
Alphabet lowerCase() { if (!hasUpperCase()) { return this; } else { checkState(!hasLowerCase(), "Cannot call lowerCase() on a mixed-case alphabet"); char[] lowerCased = new char[chars.length]; for (int i = 0; i < chars.length; i++) { lowerCased[i] = Ascii.toLowerCase(chars[i]); } return new Alphabet(name + ".lowerCase()", lowerCased); } }