/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.wildfly.security.util;
import org.wildfly.common.Assert;
import static org.wildfly.security.util.Alphabet.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.NoSuchElementException;
import javax.crypto.Mac;
/**
* A byte iterator.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public abstract class ByteIterator extends NumericIterator {
private static final int OP_BUFFER_SIZE = 8192;
private static final ThreadLocal<byte[]> OP_BUFFER = new ThreadLocal<byte[]>() {
protected byte[] initialValue() {
return new byte[OP_BUFFER_SIZE];
}
};
/**
* Determine if there are more bytes after the current byte.
*
* @return {@code true} if there are more bytes, {@code false} otherwise
*/
public abstract boolean hasNext();
/**
* Determine if there are more bytes before the current byte.
*
* @return {@code true} if there are more bytes, {@code false} otherwise
*/
public abstract boolean hasPrev();
/**
* Get the next byte.
*
* @return the next byte
* @throws NoSuchElementException if {@link #hasNext()} returns {@code false}
*/
public abstract int next() throws NoSuchElementException;
/**
* Peek at the next byte without advancing.
*
* @return the next byte
* @throws NoSuchElementException if {@link #hasNext()} returns {@code false}
*/
public abstract int peekNext() throws NoSuchElementException;
/**
* Get the previous byte.
*
* @return the previous byte
* @throws NoSuchElementException if {@link #hasPrev()} returns {@code false}
*/
public abstract int prev() throws NoSuchElementException;
/**
* Peek at the previous byte without moving backwards.
*
* @return the previous byte
* @throws NoSuchElementException if {@link #hasPrev()} returns {@code false}
*/
public abstract int peekPrev() throws NoSuchElementException;
/**
* Get the current offset, in bytes.
*
* @return the byte offset
*/
public abstract int offset();
public int getBE16() throws NoSuchElementException {
return next() << 8 | next();
}
public int getBE32() throws NoSuchElementException {
return next() << 24 | next() << 16 | next() << 8 | next();
}
public long getBE64() throws NoSuchElementException {
return (long)next() << 52 | (long)next() << 48 | (long)next() << 40 | (long)next() << 32 | (long)next() << 24 | (long)next() << 16 | (long)next() << 8 | (long)next();
}
public int getPackedBE32() throws NoSuchElementException {
int v = next();
int t = 0;
while ((v & 0x80) != 0) {
t = t << 7 | v & 0x7f;
v = next();
}
t = t << 7 | v;
return t;
}
public long getPackedBE64() throws NoSuchElementException {
int v = next();
long t = 0;
while ((v & 0x80) != 0) {
t = t << 7 | (long)(v & 0x7f);
v = next();
}
t = t << 7 | v;
return t;
}
public ByteStringBuilder appendTo(final ByteStringBuilder builder) {
final byte[] buffer = OP_BUFFER.get();
int cnt = drain(buffer);
while (cnt > 0) {
builder.append(buffer, 0, cnt);
cnt = drain(buffer);
}
return builder;
}
public void update(MessageDigest digest) throws IllegalStateException {
final byte[] buffer = OP_BUFFER.get();
int cnt = drain(buffer);
while (cnt > 0) {
digest.update(buffer, 0, cnt);
cnt = drain(buffer);
}
}
public ByteIterator doFinal(MessageDigest digest) throws IllegalStateException {
update(digest);
return ByteIterator.ofBytes(digest.digest());
}
public void update(Mac mac) throws IllegalStateException {
final byte[] buffer = OP_BUFFER.get();
int cnt = drain(buffer);
while (cnt > 0) {
mac.update(buffer, 0, cnt);
cnt = drain(buffer);
}
}
public ByteIterator doFinal(Mac mac) throws IllegalStateException {
return ByteIterator.ofBytes(mac.doFinal(drain()));
}
public void update(Signature signature) throws IllegalStateException {
final byte[] buffer = OP_BUFFER.get();
try {
int cnt = drain(buffer);
while (cnt > 0) {
signature.update(buffer, 0, cnt);
cnt = drain(buffer);
}
signature.update(drain());
} catch (SignatureException e) {
throw new IllegalStateException(e);
}
}
public ByteIterator sign(Signature signature) throws IllegalStateException {
update(signature);
try {
return ByteIterator.ofBytes(signature.sign());
} catch (SignatureException e) {
throw new IllegalStateException(e);
}
}
public boolean verify(Signature signature) throws IllegalStateException {
final byte[] buffer = OP_BUFFER.get();
try {
int cnt = drain(buffer);
while (cnt > 0) {
signature.update(buffer, 0, cnt);
cnt = drain(buffer);
}
return signature.verify(NO_BYTES);
} catch (SignatureException e) {
throw new IllegalStateException(e);
}
}
/**
* Base64-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @param alphabet the alphabet to use
* @param requirePadding {@code true} to require padding, {@code false} if padding is optional
* @return an iterator over the decoded bytes
*/
public ByteIterator base64Decode(final Base64Alphabet alphabet, boolean requirePadding) {
return super.base64Decode(alphabet, requirePadding);
}
/**
* Base64-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @param alphabet the alphabet to use
* @return an iterator over the decoded bytes
*/
public ByteIterator base64Decode(final Base64Alphabet alphabet) {
return super.base64Decode(alphabet, true);
}
/**
* Base64-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @return an iterator over the decoded bytes
*/
public ByteIterator base64Decode() {
return super.base64Decode(Base64Alphabet.STANDARD, true);
}
/**
* Base64-encode the current stream.
*
* @param alphabet the alphabet to use
* @param addPadding {@code true} to add trailing padding, {@code false} to leave it off
* @return an iterator over the encoded characters
*/
public CodePointIterator base64Encode(final Base64Alphabet alphabet, final boolean addPadding) {
if (alphabet.littleEndian) {
return new Base64EncodingCodePointIterator(addPadding) {
int calc0(final int b0) {
// d0 = r0[5..0]
return alphabet.encode(b0 & 0x3f);
}
int calc1(final int b0, final int b1) {
// d1 = r1[3..0] + r0[7..6]
return alphabet.encode((b1 << 2 | b0 >> 6) & 0x3f);
}
int calc2(final int b1, final int b2) {
// d2 = r2[1..0] + r1[7..4]
return alphabet.encode((b2 << 4 | b1 >> 4) & 0x3f);
}
int calc3(final int b2) {
// d3 = r2[7..2]
return alphabet.encode((b2 >> 2) & 0x3f);
}
};
} else {
return new Base64EncodingCodePointIterator(addPadding) {
int calc0(final int b0) {
// d0 = r0[7..2]
return alphabet.encode((b0 >> 2) & 0x3f);
}
int calc1(final int b0, final int b1) {
// d1 = r0[1..0] + r1[7..4]
return alphabet.encode((b0 << 4 | b1 >> 4) & 0x3f);
}
int calc2(final int b1, final int b2) {
// d2 = r1[3..0] + r2[7..6]
return alphabet.encode((b1 << 2 | b2 >> 6) & 0x3f);
}
int calc3(final int b2) {
// d3 = r2[5..0]
return alphabet.encode(b2 & 0x3f);
}
};
}
}
/**
* Base64-encode the current stream.
*
* @param alphabet the alphabet to use
* @return an iterator over the encoded characters
*/
public CodePointIterator base64Encode(final Base64Alphabet alphabet) {
return base64Encode(alphabet, true);
}
/**
* Base64-encode the current stream.
*
* @return an iterator over the encoded characters
*/
public CodePointIterator base64Encode() {
return base64Encode(Base64Alphabet.STANDARD, true);
}
/**
* Base32-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @param alphabet the alphabet to use
* @param requirePadding {@code true} to require padding, {@code false} if padding is optional
* @return an iterator over the decoded bytes
*/
public ByteIterator base32Decode(final Base32Alphabet alphabet, boolean requirePadding) {
return super.base32Decode(alphabet, requirePadding);
}
/**
* Base32-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @param alphabet the alphabet to use
* @return an iterator over the decoded bytes
*/
public ByteIterator base32Decode(final Base32Alphabet alphabet) {
return super.base32Decode(alphabet, true);
}
/**
* Base32-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @return an iterator over the decoded bytes
*/
public ByteIterator base32Decode() {
return super.base32Decode(Base32Alphabet.STANDARD, true);
}
/**
* Base32-encode the current stream.
*
* @param alphabet the alphabet to use
* @param addPadding {@code true} to add trailing padding, {@code false} to leave it off
* @return an iterator over the encoded characters
*/
public CodePointIterator base32Encode(final Base32Alphabet alphabet, final boolean addPadding) {
if (alphabet.littleEndian) {
return new Base32EncodingCodePointIterator(addPadding) {
int calc0(final int b0) {
// d0 = r0[4..0]
return alphabet.encode(b0 & 0x1f);
}
int calc1(final int b0, final int b1) {
// d1 = r1[1..0] + r0[7..5]
return alphabet.encode((b1 << 3 | b0 >> 5) & 0x1f);
}
int calc2(final int b1) {
// d2 = r1[6..2]
return alphabet.encode((b1 >> 2) & 0x1f);
}
int calc3(final int b1, final int b2) {
// d3 = r2[3..0] + r1[7]
return alphabet.encode((b2 << 1 | b1 >> 7) & 0x1f);
}
int calc4(final int b2, final int b3) {
// d4 = r3[0] + r2[7..4]
return alphabet.encode((b3 << 4 | b2 >> 4) & 0x1f);
}
int calc5(final int b3) {
// d5 = r3[5..1]
return alphabet.encode((b3 >> 1) & 0x1f);
}
int calc6(final int b3, final int b4) {
// d6 = r4[2..0] + r3[7..6]
return alphabet.encode((b4 << 2 | b3 >> 6) & 0x1f);
}
int calc7(final int b4) {
// d7 = r4[7..3]
return alphabet.encode((b4 >> 3) & 0x1f);
}
};
} else {
return new Base32EncodingCodePointIterator(addPadding) {
int calc0(final int b0) {
// d0 = r0[7..3]
return alphabet.encode((b0 >> 3) & 0x1f);
}
int calc1(final int b0, final int b1) {
// d1 = r0[2..0] + r1[7..6]
return alphabet.encode((b0 << 2 | b1 >> 6) & 0x1f);
}
int calc2(final int b1) {
// d2 = r1[5..1]
return alphabet.encode((b1 >> 1) & 0x1f);
}
int calc3(final int b1, final int b2) {
// d3 = r1[0] + r2[7..4]
return alphabet.encode((b1 << 4 | b2 >> 4) & 0x1f);
}
int calc4(final int b2, final int b3) {
// d4 = r2[3..0] + r3[7]
return alphabet.encode((b2 << 1 | b3 >> 7) & 0x1f);
}
int calc5(final int b3) {
// d5 = r3[6..2]
return alphabet.encode((b3 >> 2) & 0x1f);
}
int calc6(final int b3, final int b4) {
// d6 = r3[1..0] + r4[7..5]
return alphabet.encode((b3 << 3 | b4 >> 5) & 0x1f);
}
int calc7(final int b4) {
// d7 = r4[4..0]
return alphabet.encode(b4 & 0x1f);
}
};
}
}
/**
* Base32-encode the current stream.
*
* @param alphabet the alphabet to use
* @return an iterator over the encoded characters
*/
public CodePointIterator base32Encode(final Base32Alphabet alphabet) {
return base32Encode(alphabet, true);
}
/**
* Base32-encode the current stream.
*
* @return an iterator over the encoded characters
*/
public CodePointIterator base32Encode() {
return base32Encode(Base32Alphabet.STANDARD, true);
}
/**
* Hex-decode the current stream, assuming that the byte data is encoded in an ASCII-derived encoding.
*
* @return an iterator over the decoded bytes
*/
public ByteIterator hexDecode() {
return super.hexDecode();
}
/**
* Hex-encode the current stream.
*
* @param toUpperCase {@code true} to use upper case characters when encoding,
* {@code false} to use lower case characters
* @return an iterator over the encoded characters
*/
public CodePointIterator hexEncode(boolean toUpperCase) {
return new CodePointIterator() {
int b;
boolean lo;
public boolean hasNext() {
return lo || ByteIterator.this.hasNext();
}
public boolean hasPrev() {
return lo || ByteIterator.this.hasPrev();
}
private int hex(final int i) {
if (i < 10) {
return '0' + i;
} else {
assert i < 16;
return (toUpperCase ? 'A' : 'a') + i - 10;
}
}
public int next() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
if (lo) {
lo = false;
return hex(b & 0xf);
} else {
b = ByteIterator.this.next();
lo = true;
return hex(b >> 4);
}
}
public int peekNext() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
if (lo) {
return hex(b & 0xf);
} else {
return hex(ByteIterator.this.peekNext() >> 4);
}
}
public int prev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
if (lo) {
lo = false;
ByteIterator.this.prev();
return hex(b >> 4);
} else {
b = ByteIterator.this.peekPrev();
lo = true;
return hex(b & 0xf);
}
}
public int peekPrev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
if (lo) {
return hex(b >> 4);
} else {
return hex(ByteIterator.this.peekPrev() & 0xf);
}
}
public int offset() {
return ByteIterator.this.offset() * 2 + (lo ? 1 : 0);
}
};
}
/**
* Hex-encode the current stream.
*
* @return an iterator over the encoded characters
*/
public CodePointIterator hexEncode() {
return hexEncode(false);
}
/**
* Get this byte iterator as a UTF-8 string.
*
* @return the code point iterator
*/
public CodePointIterator asUtf8String() {
if (! hasNext()) {
return CodePointIterator.EMPTY;
}
return new CodePointIterator() {
private int offset = 0;
public boolean hasNext() {
return ByteIterator.this.hasNext();
}
public boolean hasPrev() {
return offset > 0;
}
private void seekToNext() {
int b;
while (ByteIterator.this.hasNext()) {
b = ByteIterator.this.next();
if ((b & 0b11_000000) != 0b10_000000) {
// back up one spot
ByteIterator.this.prev();
return;
}
}
}
private void seekToPrev() {
int b;
while (ByteIterator.this.hasPrev()) {
b = ByteIterator.this.prev();
if ((b & 0b11_000000) != 0b10_000000) {
return;
}
}
}
public int next() {
if (! ByteIterator.this.hasNext()) throw new NoSuchElementException();
offset++;
// >= 1 byte
int a = ByteIterator.this.next();
if ((a & 0b1_0000000) == 0b0_0000000) {
// one byte
return a;
}
if ((a & 0b11_000000) == 0b10_000000) {
// first byte is invalid; return � instead
seekToNext();
return '�';
}
// >= 2 bytes
if (! ByteIterator.this.hasNext()) {
// truncated
return '�';
}
int b = ByteIterator.this.next();
if ((b & 0b11_000000) != 0b10_000000) {
// second byte is invalid; return � instead
seekToNext();
return '�';
}
if ((a & 0b111_00000) == 0b110_00000) {
// two bytes
return (a & 0b000_11111) << 6 | b & 0b00_111111;
}
// >= 3 bytes
if (! ByteIterator.this.hasNext()) {
// truncated
return '�';
}
int c = ByteIterator.this.next();
if ((c & 0b11_000000) != 0b10_000000) {
// third byte is invalid; return � instead
seekToNext();
return '�';
}
if ((a & 0b1111_0000) == 0b1110_0000) {
// three bytes
return (a & 0b0000_1111) << 12 | (b & 0b00_111111) << 6 | c & 0b00_111111;
}
// >= 4 bytes
if (! ByteIterator.this.hasNext()) {
// truncated
return '�';
}
int d = ByteIterator.this.next();
if ((d & 0b11_000000) != 0b10_000000) {
// fourth byte is invalid; return � instead
seekToNext();
return '�';
}
if ((a & 0b11111_000) == 0b11110_000) {
// four bytes
return (a & 0b00000_111) << 18 | (b & 0b00_111111) << 12 | (c & 0b00_111111) << 6 | d & 0b00_111111;
}
// only invalid possibilities are left; return � instead
seekToNext();
return '�';
}
public int peekNext() throws NoSuchElementException {
if (! ByteIterator.this.hasNext()) throw new NoSuchElementException();
int a = ByteIterator.this.peekNext();
if ((a & 0b1_0000000) == 0b0_0000000) {
// one byte
return a;
}
if ((a & 0b11_000000) == 0b10_000000) {
// first byte is invalid; return � instead
return '�';
}
// >= 2 bytes
ByteIterator.this.next();
if (! ByteIterator.this.hasNext()) {
ByteIterator.this.prev();
// truncated
return '�';
}
int b = ByteIterator.this.peekNext();
if ((b & 0b11_000000) != 0b10_000000) {
// second byte is invalid; return � instead
ByteIterator.this.prev();
return '�';
}
if ((a & 0b111_00000) == 0b110_00000) {
// two bytes
ByteIterator.this.prev();
return (a & 0b000_11111) << 6 | b & 0b00_111111;
}
// >= 3 bytes
ByteIterator.this.next();
if (! ByteIterator.this.hasNext()) {
// truncated
ByteIterator.this.prev();
ByteIterator.this.prev();
return '�';
}
int c = ByteIterator.this.peekNext();
if ((c & 0b11_000000) != 0b10_000000) {
// third byte is invalid; return � instead
ByteIterator.this.prev();
ByteIterator.this.prev();
return '�';
}
if ((a & 0b1111_0000) == 0b1110_0000) {
// three bytes
ByteIterator.this.prev();
ByteIterator.this.prev();
return (a & 0b0000_1111) << 12 | (b & 0b00_111111) << 6 | c & 0b00_111111;
}
// >= 4 bytes
ByteIterator.this.next();
if (! ByteIterator.this.hasNext()) {
// truncated
ByteIterator.this.prev();
ByteIterator.this.prev();
ByteIterator.this.prev();
return '�';
}
int d = ByteIterator.this.peekNext();
if ((d & 0b11_000000) != 0b10_000000) {
// fourth byte is invalid; return � instead
ByteIterator.this.prev();
ByteIterator.this.prev();
ByteIterator.this.prev();
return '�';
}
if ((a & 0b11111_000) == 0b11110_000) {
// four bytes
ByteIterator.this.prev();
ByteIterator.this.prev();
ByteIterator.this.prev();
return (a & 0b00000_111) << 18 | (b & 0b00_111111) << 12 | (c & 0b00_111111) << 6 | d & 0b00_111111;
}
// only invalid possibilities are left; return � instead
ByteIterator.this.prev();
ByteIterator.this.prev();
ByteIterator.this.prev();
return '�';
}
public int prev() {
// read backwards
if (! ByteIterator.this.hasPrev()) throw new NoSuchElementException();
offset--;
// >= 1 byte
int a = ByteIterator.this.prev();
if ((a & 0b1_0000000) == 0b0_0000000) {
// one byte
return a;
}
if ((a & 0b11_000000) != 0b10_000000) {
// last byte is invalid; return � instead
seekToPrev();
return '�';
}
int cp = a & 0b00_111111;
// >= 2 bytes
a = ByteIterator.this.prev();
if ((a & 0b111_00000) == 0b110_00000) {
// two bytes
return (a & 0b000_11111) << 6 | cp;
}
if ((a & 0b11_000000) != 0b10_000000) {
// second-to-last byte is invalid; return � instead
seekToPrev();
return '�';
}
cp |= (a & 0b00_111111) << 6;
// >= 3 bytes
a = ByteIterator.this.prev();
if ((a & 0b1111_0000) == 0b1110_0000) {
// three bytes
return (a & 0b0000_1111) << 12 | cp;
}
if ((a & 0b11_000000) != 0b10_000000) {
// third-to-last byte is invalid; return � instead
seekToPrev();
return '�';
}
cp |= (a & 0b00_111111) << 12;
// >= 4 bytes
a = ByteIterator.this.prev();
if ((a & 0b11111_000) == 0b11110_000) {
// four bytes
return (a & 0b00000_111) << 18 | cp;
}
// only invalid possibilities are left; return � instead
seekToPrev();
return '�';
}
public int peekPrev() throws NoSuchElementException {
// read backwards
if (! ByteIterator.this.hasPrev()) throw new NoSuchElementException();
// >= 1 byte
int a = ByteIterator.this.peekPrev();
if ((a & 0b1_0000000) == 0b0_0000000) {
// one byte
return a;
}
if ((a & 0b11_000000) != 0b10_000000) {
// last byte is invalid; return � instead
return '�';
}
int cp = a & 0b00_111111;
// >= 2 bytes
ByteIterator.this.prev();
a = ByteIterator.this.peekPrev();
if ((a & 0b111_00000) == 0b110_00000) {
// two bytes
ByteIterator.this.next();
return (a & 0b000_11111) << 6 | cp;
}
if ((a & 0b11_000000) != 0b10_000000) {
// second-to-last byte is invalid; return � instead
ByteIterator.this.next();
return '�';
}
cp |= (a & 0b00_111111) << 6;
// >= 3 bytes
ByteIterator.this.prev();
a = ByteIterator.this.peekPrev();
if ((a & 0b1111_0000) == 0b1110_0000) {
// three bytes
ByteIterator.this.next();
ByteIterator.this.next();
return (a & 0b0000_1111) << 12 | cp;
}
if ((a & 0b11_000000) != 0b10_000000) {
// third-to-last byte is invalid; return � instead
ByteIterator.this.next();
ByteIterator.this.next();
return '�';
}
cp |= (a & 0b00_111111) << 12;
// >= 4 bytes
ByteIterator.this.prev();
a = ByteIterator.this.peekPrev();
if ((a & 0b11111_000) == 0b11110_000) {
// four bytes
ByteIterator.this.next();
ByteIterator.this.next();
ByteIterator.this.next();
return (a & 0b00000_111) << 18 | cp;
}
// only invalid possibilities are left; return � instead
ByteIterator.this.next();
ByteIterator.this.next();
ByteIterator.this.next();
return '�';
}
public int offset() {
return offset;
}
};
}
/**
* Get this byte iterator as a Latin-1 string.
*
* @return the code point iterator
*/
public CodePointIterator asLatin1String() {
if (! hasNext()) {
return CodePointIterator.EMPTY;
}
final int offset = offset();
return new CodePointIterator() {
public boolean hasNext() {
return ByteIterator.this.hasNext();
}
public boolean hasPrev() {
return offset > 0 && ByteIterator.this.hasPrev();
}
public int next() {
return ByteIterator.this.next();
}
public int peekNext() throws NoSuchElementException {
return ByteIterator.this.peekNext();
}
public int prev() {
if (offset == 0) throw new NoSuchElementException();
return ByteIterator.this.prev();
}
public int peekPrev() throws NoSuchElementException {
return ByteIterator.this.peekPrev();
}
public int offset() {
return ByteIterator.this.offset() - offset;
}
};
}
/**
* Determine if the remaining contents of this iterator are identical to the remaining contents of the other iterator. If the
* contents are not equal, the iterators will be positioned at the location of the first difference. If the contents
* are equal, the iterators will both be positioned at the end of their contents.
*
* @param other the other byte iterator
* @return {@code true} if the contents are equal, {@code false} otherwise
*/
public final boolean contentEquals(ByteIterator other) {
for (;;) {
if (hasNext()) {
if (! other.hasNext()) {
return false;
}
if (next() != other.next()) {
return false;
}
} else {
return ! other.hasNext();
}
}
}
/**
* Return a copy of this iterator which is limited to the given number of bytes after the current one. Advancing
* the returned iterator will also advance this one.
*
* @param size the number of bytes
* @return the limited byte iterator
*/
public final ByteIterator limitedTo(final int size) {
if (size <= 0 || ! hasNext()) {
return EMPTY;
}
return new ByteIterator() {
int offset = 0;
public boolean hasNext() {
return offset < size && ByteIterator.this.hasNext();
}
public boolean hasPrev() {
return offset > 0;
}
public int next() {
if (offset == size) {
throw new NoSuchElementException();
}
offset ++;
return ByteIterator.this.next();
}
public int peekNext() throws NoSuchElementException {
if (offset == size) {
throw new NoSuchElementException();
}
return ByteIterator.this.peekNext();
}
public int prev() {
if (offset == 0) {
throw new NoSuchElementException();
}
offset --;
return ByteIterator.this.prev();
}
public int peekPrev() throws NoSuchElementException {
if (offset == 0) {
throw new NoSuchElementException();
}
return ByteIterator.this.peekPrev();
}
public int drain(final byte[] dst, final int offs, final int len) {
return super.drain(dst, offs, Math.min(len, size - offset));
}
public int offset() {
return offset;
}
};
}
/**
* Get a sub-iterator that is delimited by the given bytes. The returned iterator offset starts at 0 and cannot
* be backed up before that point. The returned iterator will return {@code false} for {@code hasNext()} if the next
* character in the encapsulated iterator is a delimiter or if the underlying iterator returns {@code false} for
* {@code hasNext()}.
*
* @param delims the byte delimiters
* @return the sub-iterator
*/
public final ByteIterator delimitedBy(final int... delims) {
if ((delims == null) || (delims.length == 0) || ! hasNext()) {
return EMPTY;
}
for (int delim : delims) {
if (delim < 0 || delim > 0xff) {
return EMPTY;
}
}
return new ByteIterator() {
int offset = 0;
int current = -1;
public boolean hasNext() {
return ByteIterator.this.hasNext() && ! isDelim(ByteIterator.this.peekNext());
}
public boolean hasPrev() {
return offset > 0;
}
public int next() {
int n = ByteIterator.this.peekNext();
if (isDelim(n)) {
current = -1;
throw new NoSuchElementException();
}
offset ++;
return current = ByteIterator.this.next();
}
public int peekNext() throws NoSuchElementException {
int n = ByteIterator.this.peekNext();
if (isDelim(n)) {
throw new NoSuchElementException();
}
return n;
}
public int prev() {
if (offset == 0) {
current = -1;
throw new NoSuchElementException();
}
offset --;
return current = ByteIterator.this.prev();
}
public int peekPrev() throws NoSuchElementException {
if (offset == 0) {
throw new NoSuchElementException();
}
return ByteIterator.this.peekPrev();
}
public int offset() {
return offset;
}
private boolean isDelim(int b) {
for (int delim : delims) {
if (delim == b) {
return true;
}
}
return false;
}
};
}
/**
* Get a byte iterator which translates this byte iterator through an interleaving table. The table should be
* 256 entries in size or exceptions may result.
*
* @param table the interleaving table
* @return the interleaving byte iterator
*/
public ByteIterator interleavedWith(final byte[] table) {
return new ByteIterator() {
public boolean hasNext() {
return ByteIterator.this.hasNext();
}
public boolean hasPrev() {
return ByteIterator.this.hasPrev();
}
public int next() throws NoSuchElementException {
return table[ByteIterator.this.next()] & 0xff;
}
public int peekNext() throws NoSuchElementException {
return table[ByteIterator.this.peekNext()] & 0xff;
}
public int prev() throws NoSuchElementException {
return table[ByteIterator.this.prev()] & 0xff;
}
public int peekPrev() throws NoSuchElementException {
return table[ByteIterator.this.peekPrev()] & 0xff;
}
public int offset() {
return ByteIterator.this.offset();
}
};
}
/**
* Get a byte iterator which translates this byte iterator through an interleaving table. The table should be
* 256 entries in size or exceptions may result.
*
* @param table the interleaving table
* @return the interleaving byte iterator
*/
public ByteIterator interleavedWith(final int[] table) {
return new ByteIterator() {
public boolean hasNext() {
return ByteIterator.this.hasNext();
}
public boolean hasPrev() {
return ByteIterator.this.hasPrev();
}
public int next() throws NoSuchElementException {
return table[ByteIterator.this.next()] & 0xff;
}
public int peekNext() throws NoSuchElementException {
return table[ByteIterator.this.peekNext()] & 0xff;
}
public int prev() throws NoSuchElementException {
return table[ByteIterator.this.prev()] & 0xff;
}
public int peekPrev() throws NoSuchElementException {
return table[ByteIterator.this.peekPrev()] & 0xff;
}
public int offset() {
return ByteIterator.this.offset();
}
};
}
/**
* Drain all the remaining bytes in this iterator to the given stream.
*
* @param stream the stream
* @return the same stream
*/
public ByteArrayOutputStream drainTo(ByteArrayOutputStream stream) {
while (hasNext()) {
stream.write(next());
}
return stream;
}
/**
* Drain all the remaining bytes in this iterator.
*
* @return the remaining bytes as a single array
*/
public byte[] drain() {
return drainTo(new ByteArrayOutputStream()).toByteArray();
}
/**
* Drain up to {@code count} bytes from this iterator, returning the result.
*
* @param count the number of bytes to read
* @return the array of consumed bytes (may be smaller than {@code count})
*/
public byte[] drain(int count) {
if (count == 0) return NO_BYTES;
final byte[] b = new byte[count];
final int cnt = drain(b);
return cnt == 0 ? NO_BYTES : cnt < b.length ? Arrays.copyOf(b, cnt) : b;
}
/**
* Drain exactly {@code count} bytes from this iterator, returning the result.
*
* @param count the number of bytes to read
* @return the array of consumed bytes
* @throws NoSuchElementException if there are not enough bytes to fill the array
*/
public byte[] drainAll(int count) throws NoSuchElementException {
if (count == 0) return NO_BYTES;
final byte[] b = new byte[count];
final int cnt = drain(b);
if (cnt < b.length) {
throw new NoSuchElementException();
}
return b;
}
public int drain(byte[] dst) {
return drain(dst, 0, dst.length);
}
public int drain(byte[] dst, int offs, int len) {
for (int i = 0; i < len; i ++) {
if (! hasNext()) return i;
dst[offs + i] = (byte) next();
}
return len;
}
/**
* Convenience method to directly drain a certain number of bytes to a UTF-8 string. If fewer than {@code count}
* bytes are available, only the available bytes will be used to construct the string.
*
* @param count the maximum number of bytes to consume
* @return the UTF-8 string
*/
public String drainToUtf8(int count) {
return new String(drain(count), StandardCharsets.UTF_8);
}
/**
* Convenience method to directly drain a certain number of bytes to a Latin-1 string. If fewer than {@code count}
* bytes are available, only the available bytes will be used to construct the string.
*
* @param count the maximum number of bytes to consume
* @return the Latin-1 string
*/
public String drainToLatin1(int count) {
return new String(drain(count), StandardCharsets.ISO_8859_1);
}
/**
* Get a byte iterator for a byte array.
*
* @param bytes the array
* @return the byte iterator
*/
public static ByteIterator ofBytes(final byte... bytes) {
return ofBytes(bytes, 0, bytes.length);
}
/**
* Get a byte iterator for a byte array.
*
* @param bytes the array
* @param offs the array offset
* @param len the number of bytes to include
* @return the byte iterator
*/
public static ByteIterator ofBytes(final byte[] bytes, final int offs, final int len) {
if (len <= 0) {
return EMPTY;
}
return new ByteIterator() {
private int idx = 0;
public boolean hasNext() {
return idx < len;
}
public boolean hasPrev() {
return idx > 0;
}
public int next() {
if (! hasNext()) throw new NoSuchElementException();
return bytes[offs + idx++] & 0xff;
}
public int prev() {
if (! hasPrev()) throw new NoSuchElementException();
return bytes[offs + --idx] & 0xff;
}
public int peekNext() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
return bytes[offs + idx] & 0xff;
}
public int peekPrev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
return bytes[offs + idx - 1] & 0xff;
}
public int offset() {
return idx;
}
public void update(final MessageDigest digest) throws IllegalStateException {
digest.update(bytes, offs + idx, len - idx);
idx = len;
}
public ByteIterator doFinal(final MessageDigest digest) throws IllegalStateException {
update(digest);
return ByteIterator.ofBytes(digest.digest());
}
public void update(final Mac mac) throws IllegalStateException {
mac.update(bytes, offs + idx, len - idx);
idx = len;
}
public ByteIterator doFinal(final Mac mac) throws IllegalStateException {
update(mac);
return ByteIterator.ofBytes(mac.doFinal());
}
public void update(final Signature signature) throws IllegalStateException {
try {
signature.update(bytes, offs + idx, len - idx);
idx = len;
} catch (SignatureException e) {
throw new IllegalStateException(e);
}
}
public boolean verify(final Signature signature) throws IllegalStateException {
try {
return signature.verify(bytes, offs + idx, len - idx);
} catch (SignatureException e) {
throw new IllegalStateException(e);
} finally {
idx = len;
}
}
public ByteArrayOutputStream drainTo(final ByteArrayOutputStream stream) {
stream.write(bytes, offs + idx, len - idx);
idx = len;
return stream;
}
public byte[] drain() {
try {
return Arrays.copyOfRange(bytes, offs + idx, offs + len);
} finally {
idx = len;
}
}
public int drain(final byte[] dst, final int offs, final int dlen) {
int cnt = Math.min(len - idx, dlen);
System.arraycopy(bytes, offs + idx, dst, offs, cnt);
idx += cnt;
return cnt;
}
public String drainToUtf8(final int count) {
int cnt = Math.min(len - idx, count);
String s = new String(bytes, idx, cnt, StandardCharsets.UTF_8);
idx += cnt;
return s;
}
public String drainToLatin1(final int count) {
int cnt = Math.min(len - idx, count);
String s = new String(bytes, idx, cnt, StandardCharsets.ISO_8859_1);
idx += cnt;
return s;
}
public ByteStringBuilder appendTo(final ByteStringBuilder builder) {
builder.append(bytes, offs + idx, len - idx);
idx = len;
return builder;
}
};
}
/**
* Get a byte iterator for a byte array with interleave.
*
* @param bytes the array
* @param offs the array offset
* @param len the number of bytes to include
* @param interleave the interleave table to use
* @return the byte iterator
*/
public static ByteIterator ofBytes(final byte[] bytes, final int offs, final int len, final int[] interleave) {
if (len <= 0) {
return EMPTY;
}
return new ByteIterator() {
private int idx = 0;
public boolean hasNext() {
return idx < len;
}
public boolean hasPrev() {
return idx > 0;
}
public int next() {
if (!hasNext()) throw new NoSuchElementException();
return bytes[offs + interleave[idx++]] & 0xff;
}
public int prev() {
if (!hasPrev()) throw new NoSuchElementException();
return bytes[offs + interleave[--idx]] & 0xff;
}
public int peekNext() throws NoSuchElementException {
if (!hasNext()) throw new NoSuchElementException();
return bytes[offs + interleave[idx]] & 0xff;
}
public int peekPrev() throws NoSuchElementException {
if (!hasPrev()) throw new NoSuchElementException();
return bytes[offs + interleave[idx - 1]] & 0xff;
}
public int offset() {
return idx;
}
};
}
/**
* Get a byte iterator for a byte array with interleave.
*
* @param bytes the array
* @param interleave the interleave table to use
* @return the byte iterator
*/
public static ByteIterator ofBytes(final byte[] bytes, final int[] interleave) {
return ofBytes(bytes, 0, bytes.length, interleave);
}
private static final byte[] NO_BYTES = new byte[0];
/**
* The empty byte iterator.
*/
public static final ByteIterator EMPTY = new ByteIterator() {
public boolean hasNext() {
return false;
}
public boolean hasPrev() {
return false;
}
public int next() {
throw new NoSuchElementException();
}
public int peekNext() throws NoSuchElementException {
throw new NoSuchElementException();
}
public int prev() {
throw new NoSuchElementException();
}
public int peekPrev() throws NoSuchElementException {
throw new NoSuchElementException();
}
public int offset() {
return 0;
}
public byte[] drain() {
return NO_BYTES;
}
public int drain(final byte[] dst, final int offs, final int len) {
return 0;
}
};
public final InputStream asInputStream() {
return new InputStream() {
public int read() throws IOException {
return hasNext() ? next() : -1;
}
public int read(final byte[] b) throws IOException {
return drain(b);
}
public int read(final byte[] b, final int off, final int len) throws IOException {
return drain(b, off, len);
}
};
}
abstract class Base64EncodingCodePointIterator extends CodePointIterator {
private final boolean addPadding;
private int c0, c1, c2, c3;
private int state;
private int offset;
public Base64EncodingCodePointIterator(final boolean addPadding) {
this.addPadding = addPadding;
}
// states:
// 0 - need another three data bytes
// 1 - 4 characters to read
// 2 - 3 characters to read
// 3 - 2 characters to read
// 4 - 1 character to read
// 5 - 2 characters + == to read
// 6 - 1 character (c1) + == to read
// 7 - == to read
// 8 - second = to read
// 9 - 3 characters + = to read
// a - 2 characters (c1, c2) + = to read
// b - 1 character (c2) + = to read
// c - = to read
// d - after ==
// e - after =
// f - clean end
public boolean hasNext() {
return state == 0 && ByteIterator.this.hasNext() || state > 0 && state < 0xd;
}
public boolean hasPrev() {
return offset > 0;
}
abstract int calc0(int b0);
abstract int calc1(int b0, int b1);
abstract int calc2(int b1, int b2);
abstract int calc3(int b2);
public int next() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
offset++;
switch (state) {
case 0: {
assert ByteIterator.this.hasNext();
int b0 = ByteIterator.this.next();
c0 = calc0(b0);
if (!ByteIterator.this.hasNext()) {
c1 = calc1(b0, 0);
state = 6;
return c0;
}
int b1 = ByteIterator.this.next();
c1 = calc1(b0, b1);
if (!ByteIterator.this.hasNext()) {
c2 = calc2(b1, 0);
state = 0xa;
return c0;
}
int b2 = ByteIterator.this.next();
c2 = calc2(b1, b2);
c3 = calc3(b2);
state = 2;
return c0;
}
case 1: {
state = 2;
return c0;
}
case 2: {
state = 3;
return c1;
}
case 3: {
state = 4;
return c2;
}
case 4: {
state = 0;
return c3;
}
case 5: {
state = 6;
return c0;
}
case 6: {
state = addPadding ? 7 : 0xd;
return c1;
}
case 7: {
state = 8;
return '=';
}
case 8: {
state = 0xd;
return '=';
}
case 9: {
state = 0xa;
return c0;
}
case 0xa: {
state = 0xb;
return c1;
}
case 0xb: {
state = addPadding ? 0xc : 0xe;
return c2;
}
case 0xc: {
state = 0xe;
return '=';
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int peekNext() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
switch (state) {
case 0: {
assert ByteIterator.this.hasNext();
int b0 = ByteIterator.this.next();
c0 = calc0(b0);
if (!ByteIterator.this.hasNext()) {
c1 = calc1(b0, 0);
state = 5;
return c0;
}
int b1 = ByteIterator.this.next();
c1 = calc1(b0, b1);
if (!ByteIterator.this.hasNext()) {
c2 = calc2(b1, 0);
state = 9;
return c0;
}
int b2 = ByteIterator.this.next();
c2 = calc2(b1, b2);
c3 = calc3(b2);
state = 1;
return c0;
}
case 1: {
return c0;
}
case 2: {
return c1;
}
case 3: {
return c2;
}
case 4: {
return c3;
}
case 5: {
return c0;
}
case 6: {
return c1;
}
case 7: {
return '=';
}
case 8: {
return '=';
}
case 9: {
return c0;
}
case 0xa: {
return c1;
}
case 0xb: {
return c2;
}
case 0xc: {
return '=';
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int prev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
offset--;
switch (state) {
case 0:
case 1:
case 5:
case 9:
case 0xf: {
int b2 = ByteIterator.this.prev();
int b1 = ByteIterator.this.prev();
int b0 = ByteIterator.this.prev();
c0 = calc0(b0);
c1 = calc1(b0, b1);
c2 = calc2(b1, b2);
c3 = calc3(b2);
state = 4;
return c3;
}
case 2: {
state = 1;
return c0;
}
case 3: {
state = 2;
return c1;
}
case 4: {
state = 3;
return c2;
}
case 6: {
state = 5;
return c0;
}
case 7: {
state = 6;
return c1;
}
case 8: {
state = 7;
return '=';
}
case 0xa: {
state = 9;
return c0;
}
case 0xb: {
state = 0xa;
return c1;
}
case 0xc: {
state = 0xb;
return c2;
}
case 0xd: {
state = 8;
return '=';
}
case 0xe: {
state = 0xc;
return '=';
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int peekPrev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
switch (state) {
case 0:
case 1:
case 5:
case 9:
case 0xf: {
return calc3(ByteIterator.this.peekPrev());
}
case 2: {
return c0;
}
case 3: {
return c1;
}
case 4: {
return c2;
}
case 6: {
return c0;
}
case 7: {
return c1;
}
case 8: {
return '=';
}
case 0xa: {
return c0;
}
case 0xb: {
return c1;
}
case 0xc: {
return c2;
}
case 0xd: {
return '=';
}
case 0xe: {
return '=';
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int offset() {
return offset;
}
}
abstract class Base32EncodingCodePointIterator extends CodePointIterator {
private final boolean addPadding;
private int c0, c1, c2, c3, c4, c5, c6, c7;
private int state;
private int offset;
public Base32EncodingCodePointIterator(final boolean addPadding) {
this.addPadding = addPadding;
}
// states:
// 0x00 - need another five data bytes
// 0x01 - 8 characters to read
// 0x02 - 7 characters to read
// 0x03 - 6 characters to read
// 0x04 - 5 characters to read
// 0x05 - 4 characters to read
// 0x06 - 3 characters to read
// 0x07 - 2 characters to read
// 0x08 - 1 character to read
// 0x09 - 2 characters + ====== to read
// 0x0a - 1 character (c1) + ====== to read
// 0x0b - ====== to read
// 0x0c - ===== to read
// 0x0d - ==== to read
// 0x0e - === to read
// 0x0f - == to read
// 0x10 - = to read
// 0x11 - 4 characters + ==== to read
// 0x12 - 3 characters (c1, c2, c3) + ==== to read
// 0x13 - 2 characters (c2, c3) + ==== to read
// 0x14 - 1 character (c3) + ==== to read
// 0x15 - ==== to read
// 0x16 - === to read
// 0x17 - == to read
// 0x18 - = to read
// 0x19 - 5 characters + === to read
// 0x1a - 4 characters (c1, c2, c3, c4) + === to read
// 0x1b - 3 characters (c2, c3, c4) + === to read
// 0x1c - 2 characters (c3, c4) + === to read
// 0x1d - 1 character (c4) + === to read
// 0x1e - === to read
// 0x1f - == to read
// 0x20 - = to read
// 0x21 - 7 characters + = to read
// 0x22 - 6 characters (c1, c2, c3, c4, c5, c6) + = to read
// 0x23 - 5 characters (c2, c3, c4, c5, c6) + = to read
// 0x24 - 4 characters (c3, c4, c5, c6) + = to read
// 0x25 - 3 characters (c4, c5, c6) + = to read
// 0x26 - 2 characters (c5, c6) + = to read
// 0x27 - 1 characters (c6) + = to read
// 0x28 - = to read
// 0x29 - after ======
// 0x2a - after ====
// 0x2b - after ===
// 0x2c - after =
// 0x2d - end
public boolean hasNext() {
return state == 0 && ByteIterator.this.hasNext() || state > 0 && state < 0x29;
}
public boolean hasPrev() {
return offset > 0;
}
abstract int calc0(int b0);
abstract int calc1(int b0, int b1);
abstract int calc2(final int b1);
abstract int calc3(final int b1, final int b2);
abstract int calc4(final int b2, final int b3);
abstract int calc5(final int b3);
abstract int calc6(final int b3, final int b4);
abstract int calc7(final int b4);
public int next() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
offset++;
switch (state) {
case 0: {
assert ByteIterator.this.hasNext();
int b0 = ByteIterator.this.next();
c0 = calc0(b0);
if (!ByteIterator.this.hasNext()) {
c1 = calc1(b0, 0);
state = 0x0a;
return c0;
}
int b1 = ByteIterator.this.next();
c1 = calc1(b0, b1);
c2 = calc2(b1);
if (!ByteIterator.this.hasNext()) {
c3 = calc3(b1, 0);
state = 0x12;
return c0;
}
int b2 = ByteIterator.this.next();
c3 = calc3(b1, b2);
if (!ByteIterator.this.hasNext()) {
c4 = calc4(b2, 0);
state = 0x1a;
return c0;
}
int b3 = ByteIterator.this.next();
c4 = calc4(b2, b3);
c5 = calc5(b3);
if (!ByteIterator.this.hasNext()) {
c6 = calc6(b3, 0);
state = 0x22;
return c0;
}
int b4 = ByteIterator.this.next();
c6 = calc6(b3, b4);
c7 = calc7(b4);
state = 2;
return c0;
}
case 1:
case 9:
case 0x11:
case 0x19:
case 0x21: {
state ++;
return c0;
}
case 2:
case 0x12:
case 0x1a:
case 0x22: {
state ++;
return c1;
}
case 3:
case 0x13:
case 0x1b:
case 0x23: {
state ++;
return c2;
}
case 4:
case 0x1c:
case 0x24: {
state ++;
return c3;
}
case 5:
case 0x25: {
state ++;
return c4;
}
case 6:
case 0x26: {
state ++;
return c5;
}
case 7: {
state = 8;
return c6;
}
case 8: {
state = 0;
return c7;
}
case 0x0a: {
state = addPadding ? 0x0b : 0x29;
return c1;
}
case 0x14: {
state = addPadding ? 0x15 : 0x2a;
return c3;
}
case 0x1d: {
state = addPadding ? 0x1e : 0x2b;
return c4;
}
case 0x27: {
state = addPadding ? 0x28 : 0x2c;
return c6;
}
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x15:
case 0x16:
case 0x17:
case 0x1e:
case 0x1f: {
state ++;
return '=';
}
case 0x10: {
state = 0x29;
return '=';
}
case 0x18: {
state = 0x2a;
return '=';
}
case 0x20: {
state = 0x2b;
return '=';
}
case 0x28: {
state = 0x2c;
return '=';
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int peekNext() throws NoSuchElementException {
if (! hasNext()) throw new NoSuchElementException();
switch (state) {
case 0: {
assert ByteIterator.this.hasNext();
int b0 = ByteIterator.this.next();
c0 = calc0(b0);
if (!ByteIterator.this.hasNext()) {
c1 = calc1(b0, 0);
state = 9;
return c0;
}
int b1 = ByteIterator.this.next();
c1 = calc1(b0, b1);
c2 = calc2(b1);
if (!ByteIterator.this.hasNext()) {
c3 = calc3(b1, 0);
state = 0x11;
return c0;
}
int b2 = ByteIterator.this.next();
c3 = calc3(b1, b2);
if (!ByteIterator.this.hasNext()) {
c4 = calc4(b2, 0);
state = 0x19;
return c0;
}
int b3 = ByteIterator.this.next();
c4 = calc4(b2, b3);
c5 = calc5(b3);
if (!ByteIterator.this.hasNext()) {
c6 = calc6(b3, 0);
state = 0x21;
return c0;
}
int b4 = ByteIterator.this.next();
c6 = calc6(b3, b4);
c7 = calc7(b4);
state = 1;
return c0;
}
case 1:
case 9:
case 0x11:
case 0x19:
case 0x21: {
return c0;
}
case 2:
case 0x0a:
case 0x12:
case 0x1a:
case 0x22: {
return c1;
}
case 3:
case 0x13:
case 0x1b:
case 0x23: {
return c2;
}
case 4:
case 0x14:
case 0x1c:
case 0x24: {
return c3;
}
case 5:
case 0x1d:
case 0x25: {
return c4;
}
case 6:
case 0x26: {
return c5;
}
case 7:
case 0x27: {
return c6;
}
case 8: {
return c7;
}
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x1e:
case 0x1f:
case 0x20:
case 0x28: {
return '=';
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int prev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
offset--;
switch (state) {
case 0x21: {
ByteIterator.this.prev(); // skip and fall through
}
case 0x19: {
ByteIterator.this.prev(); // skip and fall through
}
case 0x11: {
ByteIterator.this.prev(); // skip and fall through
}
case 9: {
ByteIterator.this.prev(); // skip and fall through
}
case 0:
case 1:
case 0x2d: {
int b4 = ByteIterator.this.prev();
int b3 = ByteIterator.this.prev();
int b2 = ByteIterator.this.prev();
int b1 = ByteIterator.this.prev();
int b0 = ByteIterator.this.prev();
c0 = calc0(b0);
c1 = calc1(b0, b1);
c2 = calc2(b1);
c3 = calc3(b1, b2);
c4 = calc4(b2, b3);
c5 = calc5(b3);
c6 = calc6(b3, b4);
c7 = calc7(b4);
state = 8;
return c7;
}
case 2:
case 0x0a:
case 0x1a:
case 0x12:
case 0x22: {
state --;
return c0;
}
case 3:
case 0x0b:
case 0x13:
case 0x1b:
case 0x23: {
state --;
return c1;
}
case 4:
case 0x14:
case 0x1c:
case 0x24: {
state --;
return c2;
}
case 5:
case 0x15:
case 0x1d:
case 0x25: {
state --;
return c3;
}
case 6:
case 0x1e:
case 0x26: {
state --;
return c4;
}
case 7:
case 0x27: {
state --;
return c5;
}
case 8:
case 0x28: {
state --;
return c6;
}
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x16:
case 0x17:
case 0x18:
case 0x1f:
case 0x20: {
state --;
return '=';
}
case 0x29: {
if (addPadding) {
state = 0x10;
return '=';
} else {
state = 0x0a;
return c1;
}
}
case 0x2a: {
if (addPadding) {
state = 0x18;
return '=';
} else {
state = 0x14;
return c3;
}
}
case 0x2b: {
if (addPadding) {
state = 0x20;
return '=';
} else {
state = 0x1d;
return c4;
}
}
case 0x2c: {
if (addPadding) {
state = 0x28;
return '=';
} else {
state = 0x27;
return c6;
}
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int peekPrev() throws NoSuchElementException {
if (! hasPrev()) throw new NoSuchElementException();
switch (state) {
case 0x21:
ByteIterator.this.prev(); // skip and fall through
case 0x19:
ByteIterator.this.prev(); // skip and fall through
case 0x11:
ByteIterator.this.prev(); // skip and fall through
case 9:
ByteIterator.this.prev(); // skip and fall through
case 0:
case 1:
case 0x2d: {
int result = calc7(ByteIterator.this.peekPrev());
if (state == 9) {
ByteIterator.this.next();
} else if (state == 0x11) {
ByteIterator.this.next();
ByteIterator.this.next();
} else if (state == 0x19) {
ByteIterator.this.next();
ByteIterator.this.next();
ByteIterator.this.next();
} else if (state == 0x21) {
ByteIterator.this.next();
ByteIterator.this.next();
ByteIterator.this.next();
ByteIterator.this.next();
}
return result;
}
case 2:
case 0x0a:
case 0x1a:
case 0x12:
case 0x22: {
return c0;
}
case 3:
case 0x0b:
case 0x13:
case 0x1b:
case 0x23: {
return c1;
}
case 4:
case 0x14:
case 0x1c:
case 0x24: {
return c2;
}
case 5:
case 0x15:
case 0x1d:
case 0x25: {
return c3;
}
case 6:
case 0x1e:
case 0x26: {
return c4;
}
case 7:
case 0x27: {
return c5;
}
case 8:
case 0x28: {
return c6;
}
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x16:
case 0x17:
case 0x18:
case 0x1f:
case 0x20: {
return '=';
}
case 0x29: {
return addPadding ? '=' : c1;
}
case 0x2a: {
return addPadding ? '=' : c3;
}
case 0x2b: {
return addPadding ? '=' : c4;
}
case 0x2c: {
return addPadding ? '=' : c6;
}
default: {
throw Assert.impossibleSwitchCase(state);
}
}
}
public int offset() {
return offset;
}
}
}